// 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 > &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; }