diff --git a/DMRData.cpp b/DMRData.cpp index 25dea58..0b50e07 100644 --- a/DMRData.cpp +++ b/DMRData.cpp @@ -29,6 +29,7 @@ m_dstId(data.m_dstId), m_flco(data.m_flco), m_dataType(data.m_dataType), m_seqNo(data.m_seqNo), +m_missing(data.m_missing), m_n(data.m_n), m_ber(data.m_ber), m_rssi(data.m_rssi) @@ -45,6 +46,7 @@ m_dstId(0U), m_flco(FLCO_GROUP), m_dataType(0U), m_seqNo(0U), +m_missing(false), m_n(0U), m_ber(0U), m_rssi(0U) @@ -68,6 +70,7 @@ CDMRData& CDMRData::operator=(const CDMRData& data) m_flco = data.m_flco; m_dataType = data.m_dataType; m_seqNo = data.m_seqNo; + m_missing = data.m_missing; m_n = data.m_n; m_ber = data.m_ber; m_rssi = data.m_rssi; @@ -138,6 +141,16 @@ void CDMRData::setSeqNo(unsigned char seqNo) m_seqNo = seqNo; } +bool CDMRData::isMissing() const +{ + return m_missing; +} + +void CDMRData::setMissing(bool missing) +{ + m_missing = missing; +} + unsigned char CDMRData::getN() const { return m_n; diff --git a/DMRData.h b/DMRData.h index c5dc4fc..b06f416 100644 --- a/DMRData.h +++ b/DMRData.h @@ -45,6 +45,9 @@ public: unsigned char getDataType() const; void setDataType(unsigned char dataType); + bool isMissing() const; + void setMissing(bool missing); + unsigned char getBER() const; void setBER(unsigned char ber); @@ -62,6 +65,7 @@ private: FLCO m_flco; unsigned char m_dataType; unsigned char m_seqNo; + bool m_missing; unsigned char m_n; unsigned char m_ber; unsigned char m_rssi; diff --git a/DMRNetwork.cpp b/DMRNetwork.cpp index e8aef3d..00ec125 100644 --- a/DMRNetwork.cpp +++ b/DMRNetwork.cpp @@ -33,8 +33,7 @@ const unsigned int BUFFER_LENGTH = 500U; const unsigned int HOMEBREW_DATA_PACKET_LENGTH = 55U; -CDMRNetwork::CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, HW_TYPE hwType) : -m_addressStr(address), +CDMRNetwork::CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, HW_TYPE hwType, unsigned int jitter) : m_address(), m_port(port), m_id(NULL), @@ -46,6 +45,7 @@ m_socket(local), m_enabled(false), m_slot1(slot1), m_slot2(slot2), +m_jitterBuffers(NULL), m_hwType(hwType), m_status(WAITING_CONNECT), m_retryTimer(1000U, 10U), @@ -53,7 +53,6 @@ m_timeoutTimer(1000U, 60U), m_buffer(NULL), m_salt(NULL), m_streamId(NULL), -m_rxData(1000U, "DMR Network"), m_options(), m_callsign(), m_rxFrequency(0U), @@ -72,13 +71,19 @@ m_beacon(false) assert(port > 0U); assert(id > 1000U); assert(!password.empty()); + assert(jitter > 0U); m_address = CUDPSocket::lookup(address); - m_buffer = new unsigned char[BUFFER_LENGTH]; - m_salt = new unsigned char[sizeof(uint32_t)]; - m_id = new uint8_t[4U]; - m_streamId = new uint32_t[2U]; + m_buffer = new unsigned char[BUFFER_LENGTH]; + m_salt = new unsigned char[sizeof(uint32_t)]; + m_id = new uint8_t[4U]; + m_streamId = new uint32_t[2U]; + + m_jitterBuffers = new CJitterBuffer*[3U]; + + m_jitterBuffers[1U] = new CJitterBuffer("DMR Slot 1", 60U, DMR_SLOT_TIME, jitter, 256U, debug); + m_jitterBuffers[2U] = new CJitterBuffer("DMR Slot 2", 60U, DMR_SLOT_TIME, jitter, 256U, debug); m_id[0U] = id >> 24; m_id[1U] = id >> 16; @@ -94,10 +99,15 @@ m_beacon(false) CDMRNetwork::~CDMRNetwork() { + delete m_jitterBuffers[1U]; + delete m_jitterBuffers[2U]; + delete[] m_buffer; delete[] m_salt; delete[] m_streamId; delete[] m_id; + + delete[] m_jitterBuffers; } void CDMRNetwork::setOptions(const std::string& options) @@ -124,9 +134,6 @@ bool CDMRNetwork::open() { LogMessage("DMR, Opening DMR Network"); - if (m_address.s_addr == INADDR_NONE) - m_address = CUDPSocket::lookup(m_addressStr); - m_status = WAITING_CONNECT; m_timeoutTimer.stop(); m_retryTimer.start(); @@ -136,9 +143,6 @@ bool CDMRNetwork::open() void CDMRNetwork::enable(bool enabled) { - if (!enabled && m_enabled) - m_rxData.clear(); - m_enabled = enabled; } @@ -147,63 +151,52 @@ bool CDMRNetwork::read(CDMRData& data) if (m_status != RUNNING) return false; - if (m_rxData.isEmpty()) - return false; + for (unsigned int slotNo = 1U; slotNo <= 2U; slotNo++) { + unsigned int length = 0U; + B_STATUS status = BS_NO_DATA; - unsigned char length = 0U; - m_rxData.getData(&length, 1U); - m_rxData.getData(m_buffer, length); + status = m_jitterBuffers[slotNo]->getData(m_buffer, length); - // Is this a data packet? - if (::memcmp(m_buffer, "DMRD", 4U) != 0) - return false; + if (status != BS_NO_DATA) { + unsigned char seqNo = m_buffer[4U]; - unsigned char seqNo = m_buffer[4U]; + unsigned int srcId = (m_buffer[5U] << 16) | (m_buffer[6U] << 8) | (m_buffer[7U] << 0); - unsigned int srcId = (m_buffer[5U] << 16) | (m_buffer[6U] << 8) | (m_buffer[7U] << 0); + unsigned int dstId = (m_buffer[8U] << 16) | (m_buffer[9U] << 8) | (m_buffer[10U] << 0); - unsigned int dstId = (m_buffer[8U] << 16) | (m_buffer[9U] << 8) | (m_buffer[10U] << 0); + FLCO flco = (m_buffer[15U] & 0x40U) == 0x40U ? FLCO_USER_USER : FLCO_GROUP; - unsigned int slotNo = (m_buffer[15U] & 0x80U) == 0x80U ? 2U : 1U; + data.setSeqNo(seqNo); + data.setSlotNo(slotNo); + data.setSrcId(srcId); + data.setDstId(dstId); + data.setFLCO(flco); + data.setMissing(status == BS_MISSING); - // DMO mode slot disabling - if (slotNo == 1U && !m_duplex) - return false; + bool dataSync = (m_buffer[15U] & 0x20U) == 0x20U; + bool voiceSync = (m_buffer[15U] & 0x10U) == 0x10U; - // Individual slot disabling - if (slotNo == 1U && !m_slot1) - return false; - if (slotNo == 2U && !m_slot2) - return false; + if (dataSync) { + unsigned char dataType = m_buffer[15U] & 0x0FU; + data.setData(m_buffer + 20U); + data.setDataType(dataType); + data.setN(0U); + } else if (voiceSync) { + data.setData(m_buffer + 20U); + data.setDataType(DT_VOICE_SYNC); + data.setN(0U); + } else { + unsigned char n = m_buffer[15U] & 0x0FU; + data.setData(m_buffer + 20U); + data.setDataType(DT_VOICE); + data.setN(n); + } - FLCO flco = (m_buffer[15U] & 0x40U) == 0x40U ? FLCO_USER_USER : FLCO_GROUP; - - data.setSeqNo(seqNo); - data.setSlotNo(slotNo); - data.setSrcId(srcId); - data.setDstId(dstId); - data.setFLCO(flco); - - bool dataSync = (m_buffer[15U] & 0x20U) == 0x20U; - bool voiceSync = (m_buffer[15U] & 0x10U) == 0x10U; - - if (dataSync) { - unsigned char dataType = m_buffer[15U] & 0x0FU; - data.setData(m_buffer + 20U); - data.setDataType(dataType); - data.setN(0U); - } else if (voiceSync) { - data.setData(m_buffer + 20U); - data.setDataType(DT_VOICE_SYNC); - data.setN(0U); - } else { - unsigned char n = m_buffer[15U] & 0x0FU; - data.setData(m_buffer + 20U); - data.setDataType(DT_VOICE); - data.setN(n); + return true; + } } - return true; + return false; } bool CDMRNetwork::write(const CDMRData& data) @@ -254,15 +247,8 @@ bool CDMRNetwork::write(const CDMRData& data) } else if (dataType == DT_VOICE) { buffer[15U] |= data.getN(); } else { - if (dataType == DT_VOICE_LC_HEADER) { - m_streamId[slotIndex] = ::rand() + 1U; + if (dataType == DT_VOICE_LC_HEADER) count = 2U; - } - - if (dataType == DT_CSBK || dataType == DT_DATA_HEADER) { - m_streamId[slotIndex] = ::rand() + 1U; - count = 1U; - } buffer[15U] |= (0x20U | dataType); } @@ -365,6 +351,9 @@ void CDMRNetwork::close() void CDMRNetwork::clock(unsigned int ms) { + m_jitterBuffers[1U]->clock(ms); + m_jitterBuffers[2U]->clock(ms); + if (m_status == WAITING_CONNECT) { m_retryTimer.clock(ms); if (m_retryTimer.isRunning() && m_retryTimer.hasExpired()) { @@ -402,10 +391,7 @@ void CDMRNetwork::clock(unsigned int ms) if (m_enabled) { if (m_debug) CUtils::dump(1U, "Network Received", m_buffer, length); - - unsigned char len = length; - m_rxData.addData(&len, 1U); - m_rxData.addData(m_buffer, len); + receiveData(m_buffer, length); } } else if (::memcmp(m_buffer, "MSTNAK", 6U) == 0) { if (m_status == RUNNING) { @@ -506,6 +492,51 @@ void CDMRNetwork::clock(unsigned int ms) } } +void CDMRNetwork::reset(unsigned int slotNo) +{ + assert(slotNo == 1U || slotNo == 2U); + + if (slotNo == 1U) { + m_jitterBuffers[1U]->reset(); + m_streamId[0U] = ::rand() + 1U; + } else { + m_jitterBuffers[2U]->reset(); + m_streamId[1U] = ::rand() + 1U; + } +} + +void CDMRNetwork::receiveData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + unsigned int slotNo = (data[15U] & 0x80U) == 0x80U ? 2U : 1U; + + // DMO mode slot disabling + if (slotNo == 1U && !m_duplex) + return; + + // Individual slot disabling + if (slotNo == 1U && !m_slot1) + return; + if (slotNo == 2U && !m_slot2) + return; + + unsigned char dataType = data[15U] & 0x3FU; + if (dataType == (0x20U | DT_CSBK) || + dataType == (0x20U | DT_DATA_HEADER) || + dataType == (0x20U | DT_RATE_1_DATA) || + dataType == (0x20U | DT_RATE_34_DATA) || + dataType == (0x20U | DT_RATE_12_DATA)) { + // Data & CSBK frames + m_jitterBuffers[slotNo]->appendData(data, length); + } else { + // Voice frames + unsigned char seqNo = data[4U]; + m_jitterBuffers[slotNo]->addData(data, length, seqNo); + } +} + bool CDMRNetwork::writeLogin() { unsigned char buffer[8U]; diff --git a/DMRNetwork.h b/DMRNetwork.h index 9d1c52e..b9fb3fa 100644 --- a/DMRNetwork.h +++ b/DMRNetwork.h @@ -19,9 +19,9 @@ #if !defined(DMRNetwork_H) #define DMRNetwork_H +#include "JitterBuffer.h" #include "UDPSocket.h" #include "Timer.h" -#include "RingBuffer.h" #include "DMRData.h" #include "Defines.h" @@ -31,7 +31,7 @@ class CDMRNetwork { public: - CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, HW_TYPE hwType); + CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, HW_TYPE hwType, unsigned int jitter); ~CDMRNetwork(); void setOptions(const std::string& options); @@ -56,10 +56,11 @@ public: void clock(unsigned int ms); + void reset(unsigned int slotNo); + void close(); private: - std::string m_addressStr; in_addr m_address; unsigned int m_port; uint8_t* m_id; @@ -71,6 +72,7 @@ private: bool m_enabled; bool m_slot1; bool m_slot2; + CJitterBuffer** m_jitterBuffers; HW_TYPE m_hwType; enum STATUS { @@ -89,8 +91,6 @@ private: unsigned char* m_salt; uint32_t* m_streamId; - CRingBuffer m_rxData; - std::string m_options; std::string m_callsign; @@ -114,6 +114,8 @@ private: bool writePing(); bool write(const unsigned char* data, unsigned int length); + + void receiveData(const unsigned char* data, unsigned int length); }; #endif diff --git a/DMRSlot.cpp b/DMRSlot.cpp index 6e80255..acdccaf 100644 --- a/DMRSlot.cpp +++ b/DMRSlot.cpp @@ -936,6 +936,9 @@ void CDMRSlot::writeEndRF(bool writeEnd) } } + if (m_network != NULL) + m_network->reset(m_slotNo); + m_rfTimeoutTimer.stop(); m_rfTimeout = false; @@ -986,6 +989,9 @@ void CDMRSlot::writeEndNet(bool writeEnd) } } + if (m_network != NULL) + m_network->reset(m_slotNo); + m_networkWatchdog.stop(); m_netTimeoutTimer.stop(); m_packetTimer.stop(); diff --git a/Defines.h b/Defines.h index a6da8fe..c91b3dd 100644 --- a/Defines.h +++ b/Defines.h @@ -64,4 +64,10 @@ enum RPT_NET_STATE { RS_NET_DATA }; +enum B_STATUS { + BS_NO_DATA, + BS_DATA, + BS_MISSING +}; + #endif diff --git a/JitterBuffer.cpp b/JitterBuffer.cpp new file mode 100644 index 0000000..3af4f48 --- /dev/null +++ b/JitterBuffer.cpp @@ -0,0 +1,236 @@ +/* +* Copyright (C) 2017,2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "JitterBuffer.h" + +#include "Log.h" + +#include +#include +#include + +CJitterBuffer::CJitterBuffer(const std::string& name, unsigned int blockSize, unsigned int blockTime, unsigned int jitterTime, unsigned int topSequenceNumber, bool debug) : +m_name(name), +m_blockSize(blockSize), +m_blockTime(blockTime), +m_topSequenceNumber(topSequenceNumber), +m_debug(debug), +m_blockCount(0U), +m_timer(1000U, 0U, jitterTime), +m_stopWatch(), +m_running(false), +m_buffer(NULL), +m_headSequenceNumber(0U), +m_appendSequenceNumber(0U), +m_lastData(NULL), +m_lastDataLength(0U) +{ + assert(blockSize > 0U); + assert(blockTime > 0U); + assert(jitterTime > 0U); + assert(topSequenceNumber > 0U); + + m_blockCount = topSequenceNumber / 2U; + + m_buffer = new JitterEntry[m_blockCount]; + + for (unsigned int i = 0U; i < m_blockCount; i++) + m_buffer[i].m_data = new unsigned char[m_blockSize]; + + m_lastData = new unsigned char[m_blockSize]; + + reset(); +} + +CJitterBuffer::~CJitterBuffer() +{ + for (unsigned int i = 0U; i < m_blockCount; i++) + delete[] m_buffer[i].m_data; + + delete[] m_buffer; + delete[] m_lastData; +} + +bool CJitterBuffer::addData(const unsigned char* data, unsigned int length, unsigned int sequenceNumber) +{ + assert(data != NULL); + assert(length > 0U); + assert(length <= m_blockSize); + + unsigned int headSequenceNumber = m_headSequenceNumber % m_topSequenceNumber; + unsigned int tailSequenceNumber = (m_headSequenceNumber + m_blockCount) % m_topSequenceNumber; + + // Is the data out of sequence? + if (headSequenceNumber < tailSequenceNumber) { + if (sequenceNumber < headSequenceNumber || sequenceNumber >= tailSequenceNumber) { + LogDebug("%s, JitterBuffer: rejecting frame with seqNo=%u, raw=%u, head=%u, tail=%u", m_name.c_str(), sequenceNumber, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber); + return false; + } + } else { + if (sequenceNumber >= tailSequenceNumber && sequenceNumber < headSequenceNumber) { + LogDebug("%s, JitterBuffer: rejecting frame with seqNo=%u, raw=%u, head=%u, tail=%u", m_name.c_str(), sequenceNumber, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber); + return false; + } + } + + unsigned int number; + if (sequenceNumber >= headSequenceNumber) + number = sequenceNumber - headSequenceNumber; + else + number = (sequenceNumber + m_blockCount) - headSequenceNumber;; + + unsigned int index = (m_headSequenceNumber + number) % m_blockCount; + + // Do we already have the data? + if (m_buffer[index].m_length > 0U) { + LogDebug("%s, JitterBuffer: rejecting duplicate frame with seqNo=%u, pos=%u, raw=%u, head=%u, tail=%u", m_name.c_str(), sequenceNumber, index, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber); + return false; + } + + if (m_debug) + LogDebug("%s, JitterBuffer: adding frame with seqNo=%u, pos=%u, raw=%u, head=%u, tail=%u", m_name.c_str(), sequenceNumber, index, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber); + + ::memcpy(m_buffer[index].m_data, data, length); + m_buffer[index].m_length = length; + + if (!m_timer.isRunning()) { + LogDebug("%s, JitterBuffer: starting the timer", m_name.c_str()); + m_timer.start(); + } + + return true; +} + +bool CJitterBuffer::appendData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + assert(length <= m_blockSize); + + unsigned int headSequenceNumber = m_headSequenceNumber % m_topSequenceNumber; + unsigned int tailSequenceNumber = (m_headSequenceNumber + m_blockCount) % m_topSequenceNumber; + + // Is the data out of sequence? + if (headSequenceNumber < tailSequenceNumber) { + if (m_appendSequenceNumber < headSequenceNumber || m_appendSequenceNumber >= tailSequenceNumber) { + LogDebug("%s, JitterBuffer: rejecting append frame with seqNo=%u, raw=%u, head=%u, tail=%u", m_name.c_str(), m_appendSequenceNumber, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber); + return false; + } + } else { + if (m_appendSequenceNumber >= tailSequenceNumber && m_appendSequenceNumber < headSequenceNumber) { + LogDebug("%s, JitterBuffer: rejecting append frame with seqNo=%u, raw=%u, head=%u, tail=%u", m_name.c_str(), m_appendSequenceNumber, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber); + return false; + } + } + + unsigned int number; + if (m_appendSequenceNumber >= headSequenceNumber) + number = m_appendSequenceNumber - headSequenceNumber; + else + number = (m_appendSequenceNumber + m_blockCount) - headSequenceNumber;; + + unsigned int index = (m_headSequenceNumber + number) % m_blockCount; + + if (m_debug) + LogDebug("%s, JitterBuffer: appending frame with seqNo=%u, pos=%u, raw=%u, head=%u, tail=%u", m_name.c_str(), m_appendSequenceNumber, index, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber); + + ::memcpy(m_buffer[index].m_data, data, length); + m_buffer[index].m_length = length; + + if (!m_timer.isRunning()) { + LogDebug("%s, JitterBuffer: starting the timer from append", m_name.c_str()); + m_timer.start(); + } + + m_appendSequenceNumber++; + + return true; +} + +B_STATUS CJitterBuffer::getData(unsigned char* data, unsigned int& length) +{ + assert(data != NULL); + + if (!m_running) + return BS_NO_DATA; + + unsigned int sequenceNumber = m_stopWatch.elapsed() / m_blockTime + 2U; + if (m_headSequenceNumber > sequenceNumber) + return BS_NO_DATA; + + unsigned int head = m_headSequenceNumber % m_blockCount; + + m_headSequenceNumber++; + + if (m_buffer[head].m_length > 0U) { + if (m_debug) + LogDebug("%s, JitterBuffer: returning data, elapsed=%ums, raw=%u, head=%u", m_name.c_str(), m_stopWatch.elapsed(), m_headSequenceNumber - 1U, head); + + ::memcpy(data, m_buffer[head].m_data, m_buffer[head].m_length); + length = m_buffer[head].m_length; + + // Save this data in case no more data is available next time + ::memcpy(m_lastData, m_buffer[head].m_data, m_buffer[head].m_length); + m_lastDataLength = m_buffer[head].m_length; + + m_buffer[head].m_length = 0U; + + return BS_DATA; + } + + m_buffer[head].m_length = 0U; + + LogDebug("%s, JitterBuffer: no data available, elapsed=%ums, raw=%u, head=%u", m_name.c_str(), m_stopWatch.elapsed(), m_headSequenceNumber - 1U, head); + + // Return the last data frame if we have it + if (m_lastDataLength > 0U) { + LogDebug("%s, JitterBuffer: returning the last received frame", m_name.c_str()); + ::memcpy(data, m_lastData, m_lastDataLength); + length = m_lastDataLength; + + return BS_MISSING; + } + + return BS_NO_DATA; +} + +void CJitterBuffer::reset() +{ + for (unsigned int i = 0U; i < m_blockCount; i++) + m_buffer[i].m_length = 0U; + + m_headSequenceNumber = 0U; + m_appendSequenceNumber = 0U; + + m_lastDataLength = 0U; + + m_timer.stop(); + + m_running = false; +} + +void CJitterBuffer::clock(unsigned int ms) +{ + m_timer.clock(ms); + if (m_timer.isRunning() && m_timer.hasExpired()) { + if (!m_running) { + m_stopWatch.start(); + m_running = true; + } + } +} diff --git a/JitterBuffer.h b/JitterBuffer.h new file mode 100644 index 0000000..9a9f1fe --- /dev/null +++ b/JitterBuffer.h @@ -0,0 +1,68 @@ +/* +* Copyright (C) 2017,2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#if !defined(JITTERBUFFER_H) +#define JITTERBUFFER_H + +#include "StopWatch.h" +#include "Defines.h" +#include "Timer.h" + +#include + +class CJitterBuffer { +public: + CJitterBuffer(const std::string& name, unsigned int blockSize, unsigned int blockTime, unsigned int jitterTime, unsigned int topSequenceNumber, bool debug); + ~CJitterBuffer(); + + bool addData(const unsigned char* data, unsigned int length, unsigned int sequenceNumber); + bool appendData(const unsigned char* data, unsigned int length); + + B_STATUS getData(unsigned char* data, unsigned int& length); + + void reset(); + + void clock(unsigned int ms); + +private: + std::string m_name; + unsigned int m_blockSize; + unsigned int m_blockTime; + unsigned int m_topSequenceNumber; + bool m_debug; + unsigned int m_blockCount; + CTimer m_timer; + CStopWatch m_stopWatch; + bool m_running; + + struct JitterEntry + { + unsigned char* m_data; + unsigned int m_length; + }; + + JitterEntry* m_buffer; + unsigned int m_headSequenceNumber; + + unsigned int m_appendSequenceNumber; + + unsigned char* m_lastData; + unsigned int m_lastDataLength; +}; + +#endif diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index 9525ee4..c43249c 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -1218,7 +1218,7 @@ bool CMMDVMHost::createDMRNetwork() LogInfo(" Slot 2: %s", slot2 ? "enabled" : "disabled"); LogInfo(" Mode Hang: %us", m_dmrNetModeHang); - m_dmrNetwork = new CDMRNetwork(address, port, local, id, password, m_duplex, VERSION, debug, slot1, slot2, hwType); + m_dmrNetwork = new CDMRNetwork(address, port, local, id, password, m_duplex, VERSION, debug, slot1, slot2, hwType, jitter); std::string options = m_conf.getDMRNetworkOptions(); if (!options.empty()) { diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj index 1adb9bc..27a0276 100644 --- a/MMDVMHost.vcxproj +++ b/MMDVMHost.vcxproj @@ -186,6 +186,7 @@ + @@ -278,6 +279,7 @@ + @@ -335,4 +337,4 @@ - \ No newline at end of file + diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters index de9ba23..90b3b11 100644 --- a/MMDVMHost.vcxproj.filters +++ b/MMDVMHost.vcxproj.filters @@ -50,6 +50,9 @@ Header Files + + Header Files + Header Files @@ -322,6 +325,9 @@ Source Files + + Source Files + Source Files @@ -545,4 +551,4 @@ Source Files - \ No newline at end of file + diff --git a/Makefile b/Makefile index aaed744..1a007b2 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ LDFLAGS = -g OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ + DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o JitterBuffer.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o \ NXDNLookup.o NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o \ POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o \ diff --git a/Makefile.Pi b/Makefile.Pi index fe01ea6..825f2fb 100644 --- a/Makefile.Pi +++ b/Makefile.Pi @@ -9,7 +9,7 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ + DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o JitterBuffer.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o \ NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \ POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o \ diff --git a/Makefile.Pi.Adafruit b/Makefile.Pi.Adafruit index ecb0745..6c832a1 100644 --- a/Makefile.Pi.Adafruit +++ b/Makefile.Pi.Adafruit @@ -10,7 +10,7 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ + DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o JitterBuffer.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o \ NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \ POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \ diff --git a/Makefile.Pi.HD44780 b/Makefile.Pi.HD44780 index b14cf90..f5c4144 100644 --- a/Makefile.Pi.HD44780 +++ b/Makefile.Pi.HD44780 @@ -9,7 +9,7 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ + DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o JitterBuffer.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o \ NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \ POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \ diff --git a/Makefile.Pi.OLED b/Makefile.Pi.OLED index 0b08d5c..698d2cc 100644 --- a/Makefile.Pi.OLED +++ b/Makefile.Pi.OLED @@ -9,7 +9,7 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o OLED.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ + DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o JitterBuffer.o OLED.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o \ NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \ POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \ diff --git a/Makefile.Pi.PCF8574 b/Makefile.Pi.PCF8574 index 005c688..213ce60 100644 --- a/Makefile.Pi.PCF8574 +++ b/Makefile.Pi.PCF8574 @@ -10,7 +10,7 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ + DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o JitterBuffer.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o \ NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \ POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \ diff --git a/Makefile.Solaris b/Makefile.Solaris index f8319a3..dcc78ae 100644 --- a/Makefile.Solaris +++ b/Makefile.Solaris @@ -9,7 +9,7 @@ LDFLAGS = -g OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o \ + DStarSlowData.o Golay2087.o Golay24128.o Hamming.o JitterBuffer.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o \ NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o \ NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o \ QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \