|
|
|
@@ -24,6 +24,7 @@
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cassert>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <ctime>
|
|
|
|
|
|
|
|
|
|
const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
|
|
|
|
|
|
|
|
|
@@ -36,25 +37,37 @@ const unsigned int RTP_PORT = 64000U;
|
|
|
|
|
const unsigned int RTCP_PORT = 64001U;
|
|
|
|
|
|
|
|
|
|
CKenwoodNetwork::CKenwoodNetwork(const std::string& address, bool debug) :
|
|
|
|
|
m_rtcpSocket(RTCP_PORT),
|
|
|
|
|
m_rtpSocket(RTP_PORT),
|
|
|
|
|
m_stopWatch(),
|
|
|
|
|
m_rtcpSocket(RTCP_PORT),
|
|
|
|
|
m_address(),
|
|
|
|
|
m_headerSeen(false),
|
|
|
|
|
m_seen1(false),
|
|
|
|
|
m_seen2(false),
|
|
|
|
|
m_seen3(false),
|
|
|
|
|
m_seen4(false),
|
|
|
|
|
m_sacch(NULL),
|
|
|
|
|
m_sessionId(1U),
|
|
|
|
|
m_seqNo(0U),
|
|
|
|
|
m_timeStamp(0U),
|
|
|
|
|
m_ssrc(0U),
|
|
|
|
|
m_debug(debug),
|
|
|
|
|
m_timer(1000U, 0U, 200U)
|
|
|
|
|
m_startSecs(0U),
|
|
|
|
|
m_startUSecs(0U),
|
|
|
|
|
m_rtcpTimer(1000U, 0U, 200U),
|
|
|
|
|
m_hangTimer(1000U, 5U),
|
|
|
|
|
m_hangType(0U),
|
|
|
|
|
m_hangSrc(0U),
|
|
|
|
|
m_hangDst(0U)
|
|
|
|
|
{
|
|
|
|
|
assert(!address.empty());
|
|
|
|
|
|
|
|
|
|
m_address = CUDPSocket::lookup(address);
|
|
|
|
|
m_sacch = new unsigned char[10U];
|
|
|
|
|
|
|
|
|
|
::srand((unsigned int)m_stopWatch.time());
|
|
|
|
|
m_address = CUDPSocket::lookup(address);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CKenwoodNetwork::~CKenwoodNetwork()
|
|
|
|
|
{
|
|
|
|
|
delete[] m_sacch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CKenwoodNetwork::open()
|
|
|
|
@@ -72,7 +85,7 @@ bool CKenwoodNetwork::open()
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_ssrc = ::rand();
|
|
|
|
|
m_ssrc = m_rtpSocket.getLocalAddress();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
@@ -107,12 +120,12 @@ bool CKenwoodNetwork::processIcomVoiceHeader(const unsigned char* inData)
|
|
|
|
|
outData[3U] = inData[3U];
|
|
|
|
|
|
|
|
|
|
// FACCH 1+2
|
|
|
|
|
outData[4U] = outData[14U] = inData[6U];
|
|
|
|
|
outData[5U] = outData[15U] = inData[5U];
|
|
|
|
|
outData[6U] = outData[16U] = inData[8U];
|
|
|
|
|
outData[7U] = outData[17U] = inData[7U];
|
|
|
|
|
outData[8U] = outData[18U] = inData[10U];
|
|
|
|
|
outData[9U] = outData[19U] = inData[9U];
|
|
|
|
|
outData[4U] = outData[14U] = inData[6U];
|
|
|
|
|
outData[5U] = outData[15U] = inData[5U];
|
|
|
|
|
outData[6U] = outData[16U] = inData[8U];
|
|
|
|
|
outData[7U] = outData[17U] = inData[7U];
|
|
|
|
|
outData[8U] = outData[18U] = inData[10U];
|
|
|
|
|
outData[9U] = outData[19U] = inData[9U];
|
|
|
|
|
outData[10U] = outData[20U] = inData[12U];
|
|
|
|
|
outData[11U] = outData[21U] = inData[11U];
|
|
|
|
|
|
|
|
|
@@ -122,13 +135,16 @@ bool CKenwoodNetwork::processIcomVoiceHeader(const unsigned char* inData)
|
|
|
|
|
|
|
|
|
|
switch (inData[5U] & 0x3FU) {
|
|
|
|
|
case 0x01U:
|
|
|
|
|
m_timer.start();
|
|
|
|
|
writeRTCPData(type, src, dst);
|
|
|
|
|
m_hangTimer.stop();
|
|
|
|
|
m_rtcpTimer.start();
|
|
|
|
|
writeRTCPStart();
|
|
|
|
|
return writeRTPVoiceHeader(outData);
|
|
|
|
|
case 0x08U:
|
|
|
|
|
m_timer.stop();
|
|
|
|
|
writeRTCPData(type, src, dst);
|
|
|
|
|
return writeRTPVoiceTrailer(outData);
|
|
|
|
|
case 0x08U: {
|
|
|
|
|
m_hangTimer.start();
|
|
|
|
|
bool ret = writeRTPVoiceTrailer(outData);
|
|
|
|
|
writeRTCPHang(type, src, dst);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
@@ -224,23 +240,27 @@ bool CKenwoodNetwork::writeRTPVoiceHeader(const unsigned char* data)
|
|
|
|
|
buffer[0U] = 0x80U;
|
|
|
|
|
buffer[1U] = 0x66U;
|
|
|
|
|
|
|
|
|
|
m_seqNo++;
|
|
|
|
|
buffer[2U] = (m_seqNo >> 8) & 0xFFU;
|
|
|
|
|
buffer[3U] = (m_seqNo >> 0) & 0xFFU;
|
|
|
|
|
m_seqNo++;
|
|
|
|
|
|
|
|
|
|
m_timeStamp = (unsigned long)m_stopWatch.time();
|
|
|
|
|
|
|
|
|
|
buffer[4U] = (m_timeStamp >> 24) & 0xFFU;
|
|
|
|
|
buffer[5U] = (m_timeStamp >> 16) & 0xFFU;
|
|
|
|
|
buffer[6U] = (m_timeStamp >> 8) & 0xFFU;
|
|
|
|
|
buffer[7U] = (m_timeStamp >> 0) & 0xFFU;
|
|
|
|
|
m_timeStamp += 640U;
|
|
|
|
|
unsigned long timeStamp = getTimeStamp();
|
|
|
|
|
buffer[4U] = (timeStamp >> 24) & 0xFFU;
|
|
|
|
|
buffer[5U] = (timeStamp >> 16) & 0xFFU;
|
|
|
|
|
buffer[6U] = (timeStamp >> 8) & 0xFFU;
|
|
|
|
|
buffer[7U] = (timeStamp >> 0) & 0xFFU;
|
|
|
|
|
|
|
|
|
|
buffer[8U] = (m_ssrc >> 24) & 0xFFU;
|
|
|
|
|
buffer[9U] = (m_ssrc >> 16) & 0xFFU;
|
|
|
|
|
buffer[10U] = (m_ssrc >> 8) & 0xFFU;
|
|
|
|
|
buffer[11U] = (m_ssrc >> 0) & 0xFFU;
|
|
|
|
|
|
|
|
|
|
m_sessionId++;
|
|
|
|
|
buffer[12U] = m_sessionId;
|
|
|
|
|
|
|
|
|
|
buffer[13U] = 0x00U;
|
|
|
|
|
buffer[14U] = 0x00U;
|
|
|
|
|
buffer[15U] = 0x00U;
|
|
|
|
|
buffer[16U] = 0x03U;
|
|
|
|
|
buffer[17U] = 0x03U;
|
|
|
|
|
buffer[18U] = 0x04U;
|
|
|
|
@@ -267,19 +287,26 @@ bool CKenwoodNetwork::writeRTPVoiceTrailer(const unsigned char* data)
|
|
|
|
|
buffer[0U] = 0x80U;
|
|
|
|
|
buffer[1U] = 0x66U;
|
|
|
|
|
|
|
|
|
|
m_seqNo++;
|
|
|
|
|
buffer[2U] = (m_seqNo >> 8) & 0xFFU;
|
|
|
|
|
buffer[3U] = (m_seqNo >> 0) & 0xFFU;
|
|
|
|
|
|
|
|
|
|
buffer[4U] = (m_timeStamp >> 24) & 0xFFU;
|
|
|
|
|
buffer[5U] = (m_timeStamp >> 16) & 0xFFU;
|
|
|
|
|
buffer[6U] = (m_timeStamp >> 8) & 0xFFU;
|
|
|
|
|
buffer[7U] = (m_timeStamp >> 0) & 0xFFU;
|
|
|
|
|
unsigned long timeStamp = getTimeStamp();
|
|
|
|
|
buffer[4U] = (timeStamp >> 24) & 0xFFU;
|
|
|
|
|
buffer[5U] = (timeStamp >> 16) & 0xFFU;
|
|
|
|
|
buffer[6U] = (timeStamp >> 8) & 0xFFU;
|
|
|
|
|
buffer[7U] = (timeStamp >> 0) & 0xFFU;
|
|
|
|
|
|
|
|
|
|
buffer[8U] = (m_ssrc >> 24) & 0xFFU;
|
|
|
|
|
buffer[9U] = (m_ssrc >> 16) & 0xFFU;
|
|
|
|
|
buffer[10U] = (m_ssrc >> 8) & 0xFFU;
|
|
|
|
|
buffer[11U] = (m_ssrc >> 0) & 0xFFU;
|
|
|
|
|
|
|
|
|
|
buffer[12U] = m_sessionId;
|
|
|
|
|
|
|
|
|
|
buffer[13U] = 0x00U;
|
|
|
|
|
buffer[14U] = 0x00U;
|
|
|
|
|
buffer[15U] = 0x00U;
|
|
|
|
|
buffer[16U] = 0x03U;
|
|
|
|
|
buffer[17U] = 0x03U;
|
|
|
|
|
buffer[18U] = 0x04U;
|
|
|
|
@@ -306,21 +333,26 @@ bool CKenwoodNetwork::writeRTPVoiceData(const unsigned char* data)
|
|
|
|
|
buffer[0U] = 0x80U;
|
|
|
|
|
buffer[1U] = 0x66U;
|
|
|
|
|
|
|
|
|
|
m_seqNo++;
|
|
|
|
|
buffer[2U] = (m_seqNo >> 8) & 0xFFU;
|
|
|
|
|
buffer[3U] = (m_seqNo >> 0) & 0xFFU;
|
|
|
|
|
m_seqNo++;
|
|
|
|
|
|
|
|
|
|
buffer[4U] = (m_timeStamp >> 24) & 0xFFU;
|
|
|
|
|
buffer[5U] = (m_timeStamp >> 16) & 0xFFU;
|
|
|
|
|
buffer[6U] = (m_timeStamp >> 8) & 0xFFU;
|
|
|
|
|
buffer[7U] = (m_timeStamp >> 0) & 0xFFU;
|
|
|
|
|
m_timeStamp += 640U;
|
|
|
|
|
unsigned long timeStamp = getTimeStamp();
|
|
|
|
|
buffer[4U] = (timeStamp >> 24) & 0xFFU;
|
|
|
|
|
buffer[5U] = (timeStamp >> 16) & 0xFFU;
|
|
|
|
|
buffer[6U] = (timeStamp >> 8) & 0xFFU;
|
|
|
|
|
buffer[7U] = (timeStamp >> 0) & 0xFFU;
|
|
|
|
|
|
|
|
|
|
buffer[8U] = (m_ssrc >> 24) & 0xFFU;
|
|
|
|
|
buffer[9U] = (m_ssrc >> 16) & 0xFFU;
|
|
|
|
|
buffer[10U] = (m_ssrc >> 8) & 0xFFU;
|
|
|
|
|
buffer[11U] = (m_ssrc >> 0) & 0xFFU;
|
|
|
|
|
buffer[10U] = (m_ssrc >> 8) & 0xFFU;
|
|
|
|
|
buffer[11U] = (m_ssrc >> 0) & 0xFFU;
|
|
|
|
|
|
|
|
|
|
buffer[12U] = m_sessionId;
|
|
|
|
|
|
|
|
|
|
buffer[13U] = 0x00U;
|
|
|
|
|
buffer[14U] = 0x00U;
|
|
|
|
|
buffer[15U] = 0x00U;
|
|
|
|
|
buffer[16U] = 0x03U;
|
|
|
|
|
buffer[17U] = 0x02U;
|
|
|
|
|
buffer[18U] = 0x04U;
|
|
|
|
@@ -337,30 +369,59 @@ bool CKenwoodNetwork::writeRTPVoiceData(const unsigned char* data)
|
|
|
|
|
return m_rtpSocket.write(buffer, 59U, m_address, RTP_PORT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CKenwoodNetwork::writeRTCPPing()
|
|
|
|
|
bool CKenwoodNetwork::writeRTCPStart()
|
|
|
|
|
{
|
|
|
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
|
|
|
time_t now;
|
|
|
|
|
::time(&now);
|
|
|
|
|
|
|
|
|
|
m_startSecs = uint32_t(now);
|
|
|
|
|
|
|
|
|
|
SYSTEMTIME st;
|
|
|
|
|
::GetSystemTime(&st);
|
|
|
|
|
|
|
|
|
|
m_startUSecs = st.wMilliseconds * 1000U;
|
|
|
|
|
#else
|
|
|
|
|
struct timeval tod;
|
|
|
|
|
::gettimeofday(&tod, NULL);
|
|
|
|
|
|
|
|
|
|
m_startSecs = tod.tv_sec;
|
|
|
|
|
m_startUSecs = tod.tv_usec;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
unsigned char buffer[30U];
|
|
|
|
|
::memset(buffer, 0x00U, 30U);
|
|
|
|
|
|
|
|
|
|
buffer[0U] = 0x8AU;
|
|
|
|
|
buffer[1U] = 0xCCU;
|
|
|
|
|
|
|
|
|
|
buffer[2U] = 0x00U;
|
|
|
|
|
buffer[3U] = 0x06U;
|
|
|
|
|
|
|
|
|
|
buffer[4U] = (m_ssrc >> 24) & 0xFFU;
|
|
|
|
|
buffer[5U] = (m_ssrc >> 16) & 0xFFU;
|
|
|
|
|
buffer[6U] = (m_ssrc >> 8) & 0xFFU;
|
|
|
|
|
buffer[7U] = (m_ssrc >> 0) & 0xFFU;
|
|
|
|
|
buffer[6U] = (m_ssrc >> 8) & 0xFFU;
|
|
|
|
|
buffer[7U] = (m_ssrc >> 0) & 0xFFU;
|
|
|
|
|
|
|
|
|
|
buffer[8U] = 'K';
|
|
|
|
|
buffer[9U] = 'W';
|
|
|
|
|
buffer[10U] = 'N';
|
|
|
|
|
buffer[11U] = 'E';
|
|
|
|
|
|
|
|
|
|
buffer[12U] = (m_startSecs >> 24) & 0xFFU;
|
|
|
|
|
buffer[13U] = (m_startSecs >> 16) & 0xFFU;
|
|
|
|
|
buffer[14U] = (m_startSecs >> 8) & 0xFFU;
|
|
|
|
|
buffer[15U] = (m_startSecs >> 0) & 0xFFU;
|
|
|
|
|
|
|
|
|
|
buffer[16U] = (m_startUSecs >> 24) & 0xFFU;
|
|
|
|
|
buffer[17U] = (m_startUSecs >> 16) & 0xFFU;
|
|
|
|
|
buffer[18U] = (m_startUSecs >> 8) & 0xFFU;
|
|
|
|
|
buffer[19U] = (m_startUSecs >> 0) & 0xFFU;
|
|
|
|
|
|
|
|
|
|
buffer[22U] = 0x02U;
|
|
|
|
|
|
|
|
|
|
buffer[24U] = 0x01U;
|
|
|
|
|
buffer[25U] = 0x01U;
|
|
|
|
|
|
|
|
|
|
buffer[27U] = 0x0AU;
|
|
|
|
|
|
|
|
|
|
if (m_debug)
|
|
|
|
|
CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 28U);
|
|
|
|
@@ -368,33 +429,84 @@ bool CKenwoodNetwork::writeRTCPPing()
|
|
|
|
|
return m_rtcpSocket.write(buffer, 28U, m_address, RTCP_PORT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CKenwoodNetwork::writeRTCPData(unsigned char type, unsigned short src, unsigned short dst)
|
|
|
|
|
bool CKenwoodNetwork::writeRTCPPing()
|
|
|
|
|
{
|
|
|
|
|
unsigned char buffer[20U];
|
|
|
|
|
::memset(buffer, 0x00U, 20U);
|
|
|
|
|
unsigned char buffer[30U];
|
|
|
|
|
::memset(buffer, 0x00U, 30U);
|
|
|
|
|
|
|
|
|
|
buffer[0U] = 0x8BU;
|
|
|
|
|
buffer[0U] = 0x8AU;
|
|
|
|
|
buffer[1U] = 0xCCU;
|
|
|
|
|
|
|
|
|
|
buffer[3U] = 0x04U;
|
|
|
|
|
buffer[2U] = 0x00U;
|
|
|
|
|
buffer[3U] = 0x06U;
|
|
|
|
|
|
|
|
|
|
buffer[4U] = (m_ssrc >> 24) & 0xFFU;
|
|
|
|
|
buffer[5U] = (m_ssrc >> 16) & 0xFFU;
|
|
|
|
|
buffer[6U] = (m_ssrc >> 8) & 0xFFU;
|
|
|
|
|
buffer[7U] = (m_ssrc >> 0) & 0xFFU;
|
|
|
|
|
buffer[6U] = (m_ssrc >> 8) & 0xFFU;
|
|
|
|
|
buffer[7U] = (m_ssrc >> 0) & 0xFFU;
|
|
|
|
|
|
|
|
|
|
buffer[8U] = 'K';
|
|
|
|
|
buffer[9U] = 'W';
|
|
|
|
|
buffer[10U] = 'N';
|
|
|
|
|
buffer[11U] = 'E';
|
|
|
|
|
|
|
|
|
|
buffer[12U] = (src >> 8) & 0xFFU;
|
|
|
|
|
buffer[13U] = (src >> 0) & 0xFFU;
|
|
|
|
|
buffer[12U] = (m_startSecs >> 24) & 0xFFU;
|
|
|
|
|
buffer[13U] = (m_startSecs >> 16) & 0xFFU;
|
|
|
|
|
buffer[14U] = (m_startSecs >> 8) & 0xFFU;
|
|
|
|
|
buffer[15U] = (m_startSecs >> 0) & 0xFFU;
|
|
|
|
|
|
|
|
|
|
buffer[14U] = (dst >> 8) & 0xFFU;
|
|
|
|
|
buffer[15U] = (dst >> 0) & 0xFFU;
|
|
|
|
|
buffer[16U] = (m_startUSecs >> 24) & 0xFFU;
|
|
|
|
|
buffer[17U] = (m_startUSecs >> 16) & 0xFFU;
|
|
|
|
|
buffer[18U] = (m_startUSecs >> 8) & 0xFFU;
|
|
|
|
|
buffer[19U] = (m_startUSecs >> 0) & 0xFFU;
|
|
|
|
|
|
|
|
|
|
buffer[16U] = type;
|
|
|
|
|
buffer[22U] = 0x02U;
|
|
|
|
|
|
|
|
|
|
buffer[24U] = 0x01U;
|
|
|
|
|
|
|
|
|
|
buffer[27U] = 0x7BU;
|
|
|
|
|
|
|
|
|
|
if (m_debug)
|
|
|
|
|
CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 28U);
|
|
|
|
|
|
|
|
|
|
return m_rtcpSocket.write(buffer, 28U, m_address, RTCP_PORT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CKenwoodNetwork::writeRTCPHang(unsigned char type, unsigned short src, unsigned short dst)
|
|
|
|
|
{
|
|
|
|
|
m_hangType = type;
|
|
|
|
|
m_hangSrc = src;
|
|
|
|
|
m_hangDst = dst;
|
|
|
|
|
|
|
|
|
|
return writeRTCPHang();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CKenwoodNetwork::writeRTCPHang()
|
|
|
|
|
{
|
|
|
|
|
unsigned char buffer[30U];
|
|
|
|
|
::memset(buffer, 0x00U, 30U);
|
|
|
|
|
|
|
|
|
|
buffer[0U] = 0x8BU;
|
|
|
|
|
buffer[1U] = 0xCCU;
|
|
|
|
|
buffer[2U] = 0x00U;
|
|
|
|
|
buffer[3U] = 0x04U;
|
|
|
|
|
|
|
|
|
|
buffer[4U] = (m_ssrc >> 24) & 0xFFU;
|
|
|
|
|
buffer[5U] = (m_ssrc >> 16) & 0xFFU;
|
|
|
|
|
buffer[6U] = (m_ssrc >> 8) & 0xFFU;
|
|
|
|
|
buffer[7U] = (m_ssrc >> 0) & 0xFFU;
|
|
|
|
|
|
|
|
|
|
buffer[8U] = 'K';
|
|
|
|
|
buffer[9U] = 'W';
|
|
|
|
|
buffer[10U] = 'N';
|
|
|
|
|
buffer[11U] = 'E';
|
|
|
|
|
|
|
|
|
|
buffer[12U] = (m_hangSrc >> 8) & 0xFFU;
|
|
|
|
|
buffer[13U] = (m_hangSrc >> 0) & 0xFFU;
|
|
|
|
|
|
|
|
|
|
buffer[14U] = (m_hangDst >> 8) & 0xFFU;
|
|
|
|
|
buffer[15U] = (m_hangDst >> 0) & 0xFFU;
|
|
|
|
|
|
|
|
|
|
buffer[16U] = m_hangType;
|
|
|
|
|
|
|
|
|
|
if (m_debug)
|
|
|
|
|
CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 20U);
|
|
|
|
@@ -409,26 +521,23 @@ unsigned int CKenwoodNetwork::read(unsigned char* data)
|
|
|
|
|
unsigned char dummy[BUFFER_LENGTH];
|
|
|
|
|
readRTCP(dummy);
|
|
|
|
|
|
|
|
|
|
bool ret;
|
|
|
|
|
|
|
|
|
|
unsigned int len = readRTP(data);
|
|
|
|
|
if (len > 0U) {
|
|
|
|
|
switch (data[9U]) {
|
|
|
|
|
case 0x05U: // Voice header or trailer
|
|
|
|
|
ret = processKenwoodVoiceHeader(data);
|
|
|
|
|
if (!ret)
|
|
|
|
|
return 0U;
|
|
|
|
|
return 33U;
|
|
|
|
|
case 0x08U: // Voice data
|
|
|
|
|
processKenwoodVoiceData(data);
|
|
|
|
|
return 33U;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
switch (len) {
|
|
|
|
|
case 0U: // Nothing received
|
|
|
|
|
return 0U;
|
|
|
|
|
case 35U: // Voice header or trailer
|
|
|
|
|
return processKenwoodVoiceHeader(data);
|
|
|
|
|
case 47U: // Voice data
|
|
|
|
|
if (m_headerSeen)
|
|
|
|
|
return processKenwoodVoiceData(data);
|
|
|
|
|
else
|
|
|
|
|
return processKenwoodVoiceLateEntry(data);
|
|
|
|
|
case 31U: // Data
|
|
|
|
|
return processKenwoodData(data);
|
|
|
|
|
default:
|
|
|
|
|
CUtils::dump(5U, "Unknown data received from the Kenwood network", data, len);
|
|
|
|
|
return 0U;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CUtils::dump(5U, "Unknown data received from the Kenwood network", data, len);
|
|
|
|
|
return 0U;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int CKenwoodNetwork::readRTP(unsigned char* data)
|
|
|
|
@@ -444,19 +553,14 @@ unsigned int CKenwoodNetwork::readRTP(unsigned char* data)
|
|
|
|
|
return 0U;
|
|
|
|
|
|
|
|
|
|
// Check if the data is for us
|
|
|
|
|
if (m_address.s_addr != address.s_addr || port != RTP_PORT) {
|
|
|
|
|
LogMessage("Kenwood RTP packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, RTP_PORT, port);
|
|
|
|
|
if (m_address.s_addr != address.s_addr) {
|
|
|
|
|
LogMessage("Kenwood RTP packet received from an invalid source, %08X != %08X", m_address.s_addr, address.s_addr);
|
|
|
|
|
return 0U;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_debug)
|
|
|
|
|
CUtils::dump(1U, "Kenwood Network RTP Data Received", buffer, length);
|
|
|
|
|
|
|
|
|
|
if (length != 47 && length != 59) {
|
|
|
|
|
LogError("Invalid RTP length of %d", length);
|
|
|
|
|
return 0U;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
::memcpy(data, buffer + 12U, length - 12U);
|
|
|
|
|
|
|
|
|
|
return length - 12U;
|
|
|
|
@@ -475,19 +579,14 @@ unsigned int CKenwoodNetwork::readRTCP(unsigned char* data)
|
|
|
|
|
return 0U;
|
|
|
|
|
|
|
|
|
|
// Check if the data is for us
|
|
|
|
|
if (m_address.s_addr != address.s_addr || port != RTCP_PORT) {
|
|
|
|
|
LogMessage("Kenwood RTCP packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, RTCP_PORT, port);
|
|
|
|
|
if (m_address.s_addr != address.s_addr) {
|
|
|
|
|
LogMessage("Kenwood RTCP packet received from an invalid source, %08X != %08X", m_address.s_addr, address.s_addr);
|
|
|
|
|
return 0U;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_debug)
|
|
|
|
|
CUtils::dump(1U, "Kenwood Network RTCP Data Received", buffer, length);
|
|
|
|
|
|
|
|
|
|
if (length != 20 && length != 28) {
|
|
|
|
|
LogError("Invalid RTCP length of %d", length);
|
|
|
|
|
return 0U;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (::memcmp(buffer + 8U, "KWNE", 4U) != 0) {
|
|
|
|
|
LogError("Missing RTCP KWNE signature");
|
|
|
|
|
return 0U;
|
|
|
|
@@ -508,14 +607,23 @@ void CKenwoodNetwork::close()
|
|
|
|
|
|
|
|
|
|
void CKenwoodNetwork::clock(unsigned int ms)
|
|
|
|
|
{
|
|
|
|
|
m_timer.clock(ms);
|
|
|
|
|
if (m_timer.isRunning() && m_timer.hasExpired()) {
|
|
|
|
|
writeRTCPPing();
|
|
|
|
|
m_timer.start();
|
|
|
|
|
m_rtcpTimer.clock(ms);
|
|
|
|
|
if (m_rtcpTimer.isRunning() && m_rtcpTimer.hasExpired()) {
|
|
|
|
|
if (m_hangTimer.isRunning())
|
|
|
|
|
writeRTCPHang();
|
|
|
|
|
else
|
|
|
|
|
writeRTCPPing();
|
|
|
|
|
m_rtcpTimer.start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_hangTimer.clock(ms);
|
|
|
|
|
if (m_hangTimer.isRunning() && m_hangTimer.hasExpired()) {
|
|
|
|
|
m_rtcpTimer.stop();
|
|
|
|
|
m_hangTimer.stop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CKenwoodNetwork::processKenwoodVoiceHeader(unsigned char* inData)
|
|
|
|
|
unsigned int CKenwoodNetwork::processKenwoodVoiceHeader(unsigned char* inData)
|
|
|
|
|
{
|
|
|
|
|
assert(inData != NULL);
|
|
|
|
|
|
|
|
|
@@ -552,15 +660,27 @@ bool CKenwoodNetwork::processKenwoodVoiceHeader(unsigned char* inData)
|
|
|
|
|
|
|
|
|
|
switch (outData[5U] & 0x3FU) {
|
|
|
|
|
case 0x01U:
|
|
|
|
|
::memcpy(inData, outData, 33U);
|
|
|
|
|
m_headerSeen = true;
|
|
|
|
|
m_seen1 = false;
|
|
|
|
|
m_seen2 = false;
|
|
|
|
|
m_seen3 = false;
|
|
|
|
|
m_seen4 = false;
|
|
|
|
|
return 33U;
|
|
|
|
|
case 0x08U:
|
|
|
|
|
::memcpy(inData, outData, 33U);
|
|
|
|
|
return true;
|
|
|
|
|
m_headerSeen = false;
|
|
|
|
|
m_seen1 = false;
|
|
|
|
|
m_seen2 = false;
|
|
|
|
|
m_seen3 = false;
|
|
|
|
|
m_seen4 = false;
|
|
|
|
|
return 33U;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
return 0U;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CKenwoodNetwork::processKenwoodVoiceData(unsigned char* inData)
|
|
|
|
|
unsigned int CKenwoodNetwork::processKenwoodVoiceData(unsigned char* inData)
|
|
|
|
|
{
|
|
|
|
|
assert(inData != NULL);
|
|
|
|
|
|
|
|
|
@@ -642,4 +762,162 @@ void CKenwoodNetwork::processKenwoodVoiceData(unsigned char* inData)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
::memcpy(inData, outData, 33U);
|
|
|
|
|
|
|
|
|
|
return 33U;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int CKenwoodNetwork::processKenwoodData(unsigned char* inData)
|
|
|
|
|
{
|
|
|
|
|
if (inData[7U] != 0x09U && inData[7U] != 0x0BU && inData[7U] != 0x08U)
|
|
|
|
|
return 0U;
|
|
|
|
|
|
|
|
|
|
unsigned char outData[50U];
|
|
|
|
|
|
|
|
|
|
if (inData[7U] == 0x09U || inData[7U] == 0x08U) {
|
|
|
|
|
outData[0U] = 0x90U;
|
|
|
|
|
outData[1U] = inData[8U];
|
|
|
|
|
outData[2U] = inData[7U];
|
|
|
|
|
outData[3U] = inData[10U];
|
|
|
|
|
outData[4U] = inData[9U];
|
|
|
|
|
outData[5U] = inData[12U];
|
|
|
|
|
outData[6U] = inData[11U];
|
|
|
|
|
::memcpy(inData, outData, 7U);
|
|
|
|
|
return 7U;
|
|
|
|
|
} else {
|
|
|
|
|
outData[0U] = 0x90U;
|
|
|
|
|
outData[1U] = inData[8U];
|
|
|
|
|
outData[2U] = inData[7U];
|
|
|
|
|
outData[3U] = inData[10U];
|
|
|
|
|
outData[4U] = inData[9U];
|
|
|
|
|
outData[5U] = inData[12U];
|
|
|
|
|
outData[6U] = inData[11U];
|
|
|
|
|
outData[7U] = inData[14U];
|
|
|
|
|
outData[8U] = inData[13U];
|
|
|
|
|
outData[9U] = inData[16U];
|
|
|
|
|
outData[10U] = inData[15U];
|
|
|
|
|
outData[11U] = inData[18U];
|
|
|
|
|
outData[12U] = inData[17U];
|
|
|
|
|
outData[13U] = inData[20U];
|
|
|
|
|
outData[14U] = inData[19U];
|
|
|
|
|
outData[15U] = inData[22U];
|
|
|
|
|
outData[16U] = inData[21U];
|
|
|
|
|
outData[17U] = inData[24U];
|
|
|
|
|
outData[18U] = inData[23U];
|
|
|
|
|
outData[19U] = inData[26U];
|
|
|
|
|
outData[20U] = inData[25U];
|
|
|
|
|
outData[21U] = inData[28U];
|
|
|
|
|
outData[22U] = inData[27U];
|
|
|
|
|
outData[23U] = inData[29U];
|
|
|
|
|
::memcpy(inData, outData, 24U);
|
|
|
|
|
return 24U;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned long CKenwoodNetwork::getTimeStamp() const
|
|
|
|
|
{
|
|
|
|
|
unsigned long timeStamp = 0UL;
|
|
|
|
|
|
|
|
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
|
|
|
SYSTEMTIME st;
|
|
|
|
|
::GetSystemTime(&st);
|
|
|
|
|
|
|
|
|
|
unsigned int hh = st.wHour;
|
|
|
|
|
unsigned int mm = st.wMinute;
|
|
|
|
|
unsigned int ss = st.wSecond;
|
|
|
|
|
unsigned int ms = st.wMilliseconds;
|
|
|
|
|
|
|
|
|
|
timeStamp += hh * 3600U * 1000U * 80U;
|
|
|
|
|
timeStamp += mm * 60U * 1000U * 80U;
|
|
|
|
|
timeStamp += ss * 1000U * 80U;
|
|
|
|
|
timeStamp += ms * 80U;
|
|
|
|
|
#else
|
|
|
|
|
struct timeval tod;
|
|
|
|
|
::gettimeofday(&tod, NULL);
|
|
|
|
|
|
|
|
|
|
unsigned int ss = tod.tv_sec;
|
|
|
|
|
unsigned int ms = tod.tv_usec / 1000U;
|
|
|
|
|
|
|
|
|
|
timeStamp += ss * 1000U * 80U;
|
|
|
|
|
timeStamp += ms * 80U;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return timeStamp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int CKenwoodNetwork::processKenwoodVoiceLateEntry(unsigned char* inData)
|
|
|
|
|
{
|
|
|
|
|
assert(inData != NULL);
|
|
|
|
|
|
|
|
|
|
unsigned char sacch[4U];
|
|
|
|
|
sacch[0U] = inData[12U];
|
|
|
|
|
sacch[1U] = inData[11U];
|
|
|
|
|
sacch[2U] = inData[14U];
|
|
|
|
|
sacch[3U] = inData[13U];
|
|
|
|
|
|
|
|
|
|
switch (sacch[0U] & 0xC0U) {
|
|
|
|
|
case 0xC0U:
|
|
|
|
|
if (!m_seen1) {
|
|
|
|
|
unsigned int offset = 0U;
|
|
|
|
|
for (unsigned int i = 8U; i < 26U; i++, offset++) {
|
|
|
|
|
bool b = READ_BIT(sacch, i) != 0U;
|
|
|
|
|
WRITE_BIT(m_sacch, offset, b);
|
|
|
|
|
}
|
|
|
|
|
m_seen1 = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 0x80U:
|
|
|
|
|
if (!m_seen2) {
|
|
|
|
|
unsigned int offset = 18U;
|
|
|
|
|
for (unsigned int i = 8U; i < 26U; i++, offset++) {
|
|
|
|
|
bool b = READ_BIT(sacch, i) != 0U;
|
|
|
|
|
WRITE_BIT(m_sacch, offset, b);
|
|
|
|
|
}
|
|
|
|
|
m_seen2 = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 0x40U:
|
|
|
|
|
if (!m_seen3) {
|
|
|
|
|
unsigned int offset = 36U;
|
|
|
|
|
for (unsigned int i = 8U; i < 26U; i++, offset++) {
|
|
|
|
|
bool b = READ_BIT(sacch, i) != 0U;
|
|
|
|
|
WRITE_BIT(m_sacch, offset, b);
|
|
|
|
|
}
|
|
|
|
|
m_seen3 = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 0x00U:
|
|
|
|
|
if (!m_seen4) {
|
|
|
|
|
unsigned int offset = 54U;
|
|
|
|
|
for (unsigned int i = 8U; i < 26U; i++, offset++) {
|
|
|
|
|
bool b = READ_BIT(sacch, i) != 0U;
|
|
|
|
|
WRITE_BIT(m_sacch, offset, b);
|
|
|
|
|
}
|
|
|
|
|
m_seen4 = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m_seen1 || !m_seen2 || !m_seen3 || !m_seen4)
|
|
|
|
|
return 0U;
|
|
|
|
|
|
|
|
|
|
// Create a dummy header
|
|
|
|
|
// Header SACCH
|
|
|
|
|
inData[11U] = 0x10U;
|
|
|
|
|
inData[12U] = 0x01U;
|
|
|
|
|
inData[13U] = 0x00U;
|
|
|
|
|
inData[14U] = 0x00U;
|
|
|
|
|
|
|
|
|
|
// Header FACCH
|
|
|
|
|
inData[15U] = m_sacch[1U];
|
|
|
|
|
inData[16U] = m_sacch[0U];
|
|
|
|
|
inData[17U] = m_sacch[3U];
|
|
|
|
|
inData[18U] = m_sacch[2U];
|
|
|
|
|
inData[19U] = m_sacch[5U];
|
|
|
|
|
inData[20U] = m_sacch[4U];
|
|
|
|
|
inData[21U] = m_sacch[7U];
|
|
|
|
|
inData[22U] = m_sacch[6U];
|
|
|
|
|
inData[23U] = 0x00U;
|
|
|
|
|
inData[24U] = m_sacch[8U];
|
|
|
|
|
|
|
|
|
|
return processKenwoodVoiceHeader(inData);
|
|
|
|
|
}
|
|
|
|
|