// RTPAudioSink.cpp: implementation of the RTPAudioSink class.
|
//
|
//////////////////////////////////////////////////////////////////////
|
|
#include "RTPPacket.h"
|
#include "RTPAudioSink.h"
|
#include "AudioSample.h"
|
|
using namespace std;
|
|
//////////////////////////////////////////////////////////////////////
|
// Construction/Destruction
|
//////////////////////////////////////////////////////////////////////
|
|
RTPAudioSink::RTPAudioSink()
|
{
|
char subFacilityName[100];
|
sprintf(subFacilityName, "RTPAudioSink:%x", this);
|
tracer.SetSubFacilityName(subFacilityName);
|
SetTraceLevel();
|
tracer.tracef(EE, "Constructor begin\n");
|
rtpSocket = NULL;
|
destinationIP = 0;
|
destinationIPString = "127.0.0.1";
|
destinationPort = 0;
|
localPort = 0;
|
tos = 0;
|
bRunning = false;
|
InitializeCriticalSection(&filterMutex);
|
WSADATA wsaData;
|
int result = WSAStartup(MAKEWORD(1,1), &wsaData);
|
lastCodec = -1;
|
tracer.tracef(EE, "Constructor end : 0x%x\n", result);
|
}
|
|
RTPAudioSink::~RTPAudioSink()
|
{
|
tracer.tracef(EE, "Destructor begin\n");
|
int result(0);
|
EnterCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "RTPAudioSink~ : entered filterMutex\n");
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "RTPAudioSink~ : left filterMutex\n");
|
result = CloseSocket();
|
result = WSACleanup();
|
DeleteCriticalSection(&filterMutex);
|
tracer.tracef(EE, "Destructor end : 0x%x\n", result);
|
}
|
|
int
|
RTPAudioSink::SetTraceLevel()
|
{
|
long SystemMask = 0;
|
if ((SystemMask = GetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\MTC\\Tracing", "AllComponents", 0x0)) == 0)
|
{
|
SystemMask = GetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\MTC\\Tracing", "RTPAudioSink", 0x100000);
|
}
|
tracer.SetSystemMask(SystemMask);
|
return 0;
|
}
|
|
|
int
|
RTPAudioSink::SetCodecInRegistry(char *codecName)
|
{
|
SetRegKeyString(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "RTPTransmitCodec", codecName);
|
return 0;
|
}
|
|
int
|
RTPAudioSink::SetDestination(std::string ipAddress, unsigned short udpPort)
|
{
|
tracer.tracef(EE, "SetDestination %s %u\n", ipAddress.c_str(), udpPort);
|
EnterCriticalSection(&filterMutex);
|
int result = 0;
|
destinationIPString = ipAddress;
|
destinationPort = udpPort;
|
|
// hostent *hostEnt = gethostbyname(ipAddress.c_str());
|
// if (hostEnt != NULL)
|
// {
|
// destinationIP = *((long *)(hostEnt->h_addr));
|
// }
|
// else
|
// {
|
destinationIP = inet_addr(ipAddress.c_str());
|
// }
|
|
if (!rtpSocket)
|
{
|
int result = CreateSocket();
|
}
|
if (result == 0)
|
{
|
memset((char *)&destinationSockAddr, 0, sizeof(destinationSockAddr));
|
destinationSockAddr.sin_family = AF_INET;
|
destinationSockAddr.sin_port = htons(destinationPort);
|
destinationSockAddr.sin_addr.S_un.S_addr = destinationIP;
|
// result = connect(rtpSocket, (sockaddr *)&destinationAddr, sizeof(destinationAddr));
|
}
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(EE, "~SetDestination : %d 0x%x\n", result, result);
|
return result;
|
}
|
|
int
|
RTPAudioSink::SetTOS(unsigned char tosByte)
|
{
|
tracer.tracef(EE, "SetTOS 0x%x\n", tosByte);
|
EnterCriticalSection(&filterMutex);
|
int result = 0;
|
tos = tosByte;
|
int tosValueInt = (int)tos;
|
int tosLen = sizeof(int);
|
if (rtpSocket)
|
{
|
tracer.tracef(SDI_LEVEL_ARBITRARY, "SetTOS : setsockopt\n");
|
result = setsockopt(rtpSocket, IPPROTO_IP, IP_TOS, (char *)&tosValueInt, tosLen);
|
if (result != 0)
|
{
|
result = WSAGetLastError();
|
tracer.tracef(ERR, "SetTOS : e-setsockopt 0x%x\n", result);
|
}
|
}
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(EE, "~SetTOS : %d 0x%x\n", result, result);
|
return result;
|
}
|
|
int
|
RTPAudioSink::SetLocalPort(unsigned short port)
|
{
|
tracer.tracef(EE, "SetLocalPort %u : localPort %u\n", port, localPort);
|
EnterCriticalSection(&filterMutex);
|
int result(0);
|
if (localPort != port)
|
{
|
localPort = port;
|
if (rtpSocket)
|
{
|
result = CloseSocket();
|
if (result == 0)
|
{
|
result = CreateSocket();
|
if (result == 0)
|
{
|
result = SetDestination(destinationIPString, destinationPort);
|
}
|
}
|
}
|
}
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(EE, "~SetLocalPort : %d 0x%x\n", result, result);
|
return result;
|
}
|
|
int
|
RTPAudioSink::CreateSocket()
|
{
|
tracer.tracef(EE, "CreateSocket\n");
|
EnterCriticalSection(&filterMutex);
|
int result;
|
|
if (rtpSocket)
|
{
|
result = CloseSocket();
|
}
|
rtpSocket = socket(AF_INET, SOCK_DGRAM, 0);
|
|
sockaddr_in localSockAddr;
|
memset((char *)&localSockAddr, 0, sizeof(localSockAddr));
|
localSockAddr.sin_family = AF_INET;
|
localSockAddr.sin_port = htons(localPort);
|
localSockAddr.sin_addr.S_un.S_addr = INADDR_ANY;
|
result = bind(rtpSocket, (sockaddr *)&localSockAddr, sizeof(localSockAddr));
|
if (result != 0)
|
{
|
result = WSAGetLastError();
|
tracer.tracef(ERR, "CreateSocket : e-bind : 0x%x\n", result);
|
}
|
else
|
{
|
sockaddr_in boundSockAddr;
|
int sizeOfBoundSockAddr = sizeof(boundSockAddr);
|
result = getsockname(rtpSocket, (sockaddr *)&boundSockAddr, &sizeOfBoundSockAddr);
|
if (result == 0)
|
{
|
SetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "TransmitPort", ntohs(boundSockAddr.sin_port));
|
}
|
else
|
{
|
result = WSAGetLastError();
|
tracer.tracef(ERR, "CreateSocket : e-getsockname : %d 0x%x\n", result, result);
|
}
|
}
|
result = setsockopt(rtpSocket, IPPROTO_IP, IP_TOS, (char *)&tos, 1);
|
if (result != 0)
|
{
|
result = WSAGetLastError();
|
tracer.tracef(ERR, "CreateSocket : e-setsockopt : %d 0x%x\n", result, result);
|
}
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(EE, "~CreateSocket : %d 0x%x\n", result, result);
|
return result;
|
}
|
|
|
int
|
RTPAudioSink::CloseSocket()
|
{
|
tracer.tracef(EE, "CloseSocket\n");
|
EnterCriticalSection(&filterMutex);
|
int result(0);
|
if (rtpSocket)
|
{
|
tracer.tracef(ARB, "CloseSocket : closesocket\n");
|
result = closesocket(rtpSocket);
|
if (result != 0)
|
{
|
tracer.tracef(ERR, "CloseSocket : e-closesocket : 0x%x\n", WSAGetLastError());
|
result = -10;
|
}
|
}
|
rtpSocket = NULL;
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(EE, "~CloseSocket : %d 0x%x\n", result, result);
|
return 0;
|
}
|
|
int
|
RTPAudioSink::RenderAudioSamples(std::vector<std::pair<AudioSample*, AudioSource* > > &data)
|
{
|
tracer.tracef(DET, "RenderAudioSamples\n");
|
AudioSample *sample = NULL;
|
if (data.size() > 0)
|
{
|
sample = data[0].first;
|
}
|
if (sample)
|
{
|
int dataSize = 0;
|
int durationMillisec;
|
|
char RTPPayloadType;
|
bool transmitPacket = false; // whether a packet will be transmitted
|
bool setMarkerBit = false;
|
|
int numBytes = sample->DataSize();
|
durationMillisec = sample->GetDuration();
|
if (durationMillisec <= 0)
|
{
|
durationMillisec = sample->GetSilenceDuration();
|
}
|
tracer.tracef(DET, "RenderAudioSamples : durationMillisec = %d\n", durationMillisec);
|
|
timestamp += durationMillisec * 8;
|
seqno = (seqno + 1) % 65536;
|
|
if (numBytes > 0)
|
{
|
transmitPacket = true;
|
setMarkerBit = silenceInterval;
|
silenceInterval = false;
|
|
tracer.tracef(DET, "RenderAudioSamples : memcpy(0x%x, 0x%x, %d)\n", sendBuffer+12, sample->Data(), sample->DataSize());
|
memcpy(sendBuffer+12, sample->Data(), sample->DataSize());
|
dataSize = 12 + sample->DataSize();
|
|
WAVEFORMATEX wfx;
|
sample->GetFormat(&wfx);
|
bool writeCodec = false;
|
if (lastCodec != wfx.wFormatTag)
|
{
|
lastCodec = wfx.wFormatTag;
|
writeCodec = true;
|
}
|
switch(wfx.wFormatTag)
|
{
|
case WAVE_FORMAT_MULAW:
|
RTPPayloadType = 0x00;
|
if (writeCodec) SetCodecInRegistry("G.711 ULaw");
|
break;
|
case WAVE_FORMAT_ALAW:
|
RTPPayloadType = 0x08;
|
if (writeCodec) SetCodecInRegistry("G.711 ALaw");
|
break;
|
case WAVE_FORMAT_CISCO_G729:
|
RTPPayloadType = 0x12;
|
if (writeCodec) SetCodecInRegistry("G.729");
|
break;
|
case WAVE_FORMAT_CISCO_G723_53:
|
RTPPayloadType = 0x04;
|
if (writeCodec) SetCodecInRegistry("G.723 5.3kbps");
|
break;
|
case WAVE_FORMAT_CISCO_G723_63:
|
RTPPayloadType = 0x04;
|
if (writeCodec) SetCodecInRegistry("G.723 6.4kbps");
|
break;
|
default:
|
char codecName[100];
|
sprintf(codecName, "WAVE_FORMAT %u (ERROR)");
|
if (writeCodec) SetCodecInRegistry(codecName);
|
break;
|
}
|
}
|
else if (!silenceInterval)
|
{
|
transmitPacket = true;
|
silenceInterval = true;
|
RTPPayloadType = RTP_PAYLOADTYPE_SID;
|
// See internet draft "RTP Payload for Comfort Noise". Actually 1 byte of data (although min amount
|
// of data in RTP packet is 4 bytes. First byte is important. all others should be zero.
|
// first byte = value between 0 to 127. If it is X, then noise level is -X dB. so 0 is loudest, and
|
// 127 is quietest.
|
// Pascal Huart in Dallas gave me this information
|
*((long *)(sendBuffer + 12)) = htonl(0);
|
sendBuffer[12] = 100;
|
dataSize = 12 + 4;
|
}
|
|
if (transmitPacket)
|
{
|
sendBuffer[0] = 0x80;
|
sendBuffer[1] = RTPPayloadType;
|
if (setMarkerBit)
|
{
|
sendBuffer[1] |= 0x80;
|
}
|
*((short *)(sendBuffer + 2)) = htons((short)seqno);
|
*((long *)(sendBuffer + 4)) = htonl((long)timestamp);
|
*((long *)(sendBuffer + 8)) = htonl((long)ssrc);
|
|
tracer.tracef(DET, "RenderAudioSamples : sending %d bytes\n", dataSize);
|
EnterCriticalSection(&filterMutex);
|
if (rtpSocket)
|
{
|
sendto(rtpSocket, (char *)sendBuffer, dataSize, 0, (struct sockaddr *)(&destinationSockAddr), sizeof(destinationSockAddr));
|
}
|
LeaveCriticalSection(&filterMutex);
|
}
|
tracer.tracef(DET, "~RenderAudioSamples : seqno = %d, timeStamp = %d\n", seqno, timestamp);
|
}
|
else
|
{
|
tracer.tracef(ERR, "~RenderAudioSample : asked to transmit NULL sample\n");
|
}
|
return 0;
|
}
|
|
int
|
RTPAudioSink::PrepareSink()
|
{
|
int result(0);
|
int returnCode(0);
|
tracer.tracef(EE, "PrepareSink\n");
|
srand((unsigned int)GetTickCount());
|
ssrc = (unsigned long)rand();
|
timestamp = (unsigned long)rand();
|
seqno = (unsigned short)((unsigned long)(rand() % 65536));
|
silenceInterval = true; // so that the marker bit is set after the first packet
|
SetRegKeyString(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "DestinationAddress", destinationIPString.c_str());
|
SetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "DestinationPort", destinationPort);
|
tracer.tracef(EE, "PrepareSink : returning %d\n", result);
|
return result;
|
}
|
|
int
|
RTPAudioSink::UnprepareSink()
|
{
|
int result(0);
|
int returnCode(0);
|
tracer.tracef(EE, "UnprepareSink\n");
|
SetRegKeyString(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "DestinationAddress", "");
|
SetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "DestinationPort", 0);
|
returnCode = CloseSocket();
|
if (returnCode < 0)
|
{
|
result = -10;
|
}
|
tracer.tracef(EE, "UnprepareSink : returning %d\n", result);
|
return result;
|
}
|
|
int
|
RTPAudioSink::SinkStarted()
|
{
|
int result(0);
|
int returnCode(0);
|
tracer.tracef(EE, "SinkStarted\n");
|
EnterCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SinkStarted : entered filterMutex\n");
|
do
|
{
|
if (bRunning)
|
{
|
tracer.tracef(ERR, "SinkStarted : e-error : already running\n");
|
result = -10;
|
break;
|
}
|
returnCode = PrepareSink();
|
if (returnCode != 0)
|
{
|
tracer.tracef(ERR, "SinkStarted : e-PrepareSink : %d 0x%x\n", returnCode, returnCode);
|
result = -20;
|
break;
|
}
|
bRunning = true;
|
}
|
while (false);
|
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SinkStarted : left filterMutex\n");
|
tracer.tracef(EE, "~SinkStarted : returning %d\n", result);
|
return result;
|
}
|
|
int
|
RTPAudioSink::SinkStopped()
|
{
|
int result(0);
|
int returnCode(0);
|
tracer.tracef(EE, "SinkStopped\n");
|
EnterCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SinkStopped : entered filterMutex\n");
|
do
|
{
|
if (!bRunning)
|
{
|
tracer.tracef(ERR, "SinkStopped : e-error : not running\n");
|
result = -10;
|
break;
|
}
|
returnCode = UnprepareSink();
|
if (returnCode != 0)
|
{
|
tracer.tracef(ERR, "SinkStopped : e-UnprepareSink : %d 0x%x\n", returnCode, returnCode);
|
result = -20;
|
break;
|
}
|
bRunning = false;
|
}
|
while (false);
|
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SinkStopped : left filterMutex\n");
|
SetCodecInRegistry("");
|
lastCodec = -1;
|
tracer.tracef(EE, "~SinkStopped : returning %d\n", result);
|
return result;
|
}
|
|
int
|
RTPAudioSink::SinkThreadStarted(HANDLE sinkThreadHandle, DWORD sinkThreadID)
|
{
|
int returnCode(0);
|
int result(0);
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SinkThreadStarted\n");
|
EnterCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SinkThreadStarted : entered filterMutex\n");
|
do
|
{
|
|
returnCode = SetThreadPriority(sinkThreadHandle, THREAD_PRIORITY_TIME_CRITICAL);
|
if (returnCode == 0)
|
{
|
returnCode = GetLastError();
|
tracer.tracef(ERR, "SinkThreadStarted : e-SetThreadPriority : %d 0x%x\n", returnCode, returnCode);
|
}
|
returnCode = PrepareSink();
|
if (returnCode != 0)
|
{
|
tracer.tracef(ERR, "SinkThreadStarted : e-PrepareSink : %d 0x%x\n", returnCode, returnCode);
|
result = -10;
|
break;
|
}
|
bRunning = true;
|
}
|
while(false);
|
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SinkThreadStarted : left filterMutex\n");
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~SinkThreadStarted : %d 0x%x\n", result, result);
|
return result;
|
}
|
|
int
|
RTPAudioSink::SinkThreadStopped(HANDLE sinkThreadHandle, DWORD sinkThreadID)
|
{
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SinkThreadStopped\n");
|
int result(0);
|
int returnCode(0);
|
EnterCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SinkThreadStopped : entered filterMutex\n");
|
do
|
{
|
if (!bRunning)
|
{
|
tracer.tracef(ERR, "SinkThreadStopped : not running\n");
|
result = -10;
|
break;
|
}
|
returnCode = UnprepareSink();
|
if (returnCode != 0)
|
{
|
tracer.tracef(ERR, "SinkThreadStopped : e-UnprepareSink : %d 0x%x\n", returnCode, returnCode);
|
result = -20;
|
break;
|
}
|
bRunning = false;
|
}
|
while(false);
|
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SinkThreadStopped : left filterMutex\n");
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~SinkThreadStopped : %d 0x%x\n", result, result);
|
return result;
|
}
|