From 7e1d63dd9fd149e4934bf77095c8610fac786b04 Mon Sep 17 00:00:00 2001 From: Henri Herscher Date: Thu, 20 Oct 2005 13:40:58 +0000 Subject: First checkin git-svn-id: https://oreka.svn.sourceforge.net/svnroot/oreka/trunk@2 09dcff7a-b715-0410-9601-b79a96267cd0 --- orkaudio/audiocaptureplugins/voip/Makefile.am | 10 + .../audiocaptureplugins/voip/PacketHeaderDefs.h | 71 ++++ orkaudio/audiocaptureplugins/voip/Rtp.cpp | 257 ++++++++++++ orkaudio/audiocaptureplugins/voip/Rtp.h | 77 ++++ orkaudio/audiocaptureplugins/voip/SipSession.cpp | 282 +++++++++++++ orkaudio/audiocaptureplugins/voip/SipSession.h | 87 ++++ orkaudio/audiocaptureplugins/voip/VoIp.cpp | 465 +++++++++++++++++++++ orkaudio/audiocaptureplugins/voip/VoIp.dsp | 158 +++++++ orkaudio/audiocaptureplugins/voip/VoIpConfig.cpp | 127 ++++++ orkaudio/audiocaptureplugins/voip/VoIpConfig.h | 68 +++ orkaudio/audiocaptureplugins/voip/g711.c | 285 +++++++++++++ orkaudio/audiocaptureplugins/voip/g711.h | 21 + 12 files changed, 1908 insertions(+) create mode 100644 orkaudio/audiocaptureplugins/voip/Makefile.am create mode 100644 orkaudio/audiocaptureplugins/voip/PacketHeaderDefs.h create mode 100644 orkaudio/audiocaptureplugins/voip/Rtp.cpp create mode 100644 orkaudio/audiocaptureplugins/voip/Rtp.h create mode 100644 orkaudio/audiocaptureplugins/voip/SipSession.cpp create mode 100644 orkaudio/audiocaptureplugins/voip/SipSession.h create mode 100644 orkaudio/audiocaptureplugins/voip/VoIp.cpp create mode 100644 orkaudio/audiocaptureplugins/voip/VoIp.dsp create mode 100644 orkaudio/audiocaptureplugins/voip/VoIpConfig.cpp create mode 100644 orkaudio/audiocaptureplugins/voip/VoIpConfig.h create mode 100644 orkaudio/audiocaptureplugins/voip/g711.c create mode 100644 orkaudio/audiocaptureplugins/voip/g711.h (limited to 'orkaudio/audiocaptureplugins/voip') diff --git a/orkaudio/audiocaptureplugins/voip/Makefile.am b/orkaudio/audiocaptureplugins/voip/Makefile.am new file mode 100644 index 0000000..a11ff07 --- /dev/null +++ b/orkaudio/audiocaptureplugins/voip/Makefile.am @@ -0,0 +1,10 @@ +METASOURCES = AUTO +lib_LTLIBRARIES = libvoip.la +libvoip_la_SOURCES = VoIpConfig.cpp VoIp.cpp g711.c Rtp.cpp SipSession.cpp \ + AudioCapturePluginCommon.cpp +libvoip_la_LDFLAGS = -module +AM_CPPFLAGS = -D_REENTRANT +libvoip_la_LIBADD = -lACE -lxerces-c -llog4cxx -lorkbase -lpcap +INCLUDES = -I@top_srcdir@ -I../../../orkbasecxx -I../common +AudioCapturePluginCommon.cpp: + ln -s ../common/AudioCapturePluginCommon.cpp AudioCapturePluginCommon.cpp diff --git a/orkaudio/audiocaptureplugins/voip/PacketHeaderDefs.h b/orkaudio/audiocaptureplugins/voip/PacketHeaderDefs.h new file mode 100644 index 0000000..1293c5c --- /dev/null +++ b/orkaudio/audiocaptureplugins/voip/PacketHeaderDefs.h @@ -0,0 +1,71 @@ +/* + * Oreka -- A media capture and retrieval platform + * + * Copyright (C) 2005, orecx LLC + * + * http://www.orecx.com + * + * This program is free software, distributed under the terms of + * the GNU General Public License. + * Please refer to http://www.gnu.org/copyleft/gpl.html + * + */ + + +#ifndef __PACKETHEADERDEFS_H__ +#define __PACKETHEADERDEFS_H__ + + +// Structure of Ethernet header +typedef struct +{ + unsigned char sourceMac[6]; + unsigned char destinationMac[6]; + unsigned short length; + +} EthernetHeaderStruct; + +// Structure of an internet header, naked of options, only valid for little endian +typedef struct +{ + unsigned char ip_hl:4; // Header length + unsigned char ip_v:4; // IP protocol version + unsigned char ip_tos; // Type of service + unsigned short ip_len; // Total length + unsigned short ip_id; // Identification + unsigned short ip_off; // Fragment offset field + unsigned char ip_ttl; // Time to live + unsigned char ip_p; // Protocol + unsigned short ip_sum; // Header checksum + struct in_addr ip_src; // Source address + struct in_addr ip_dest; // Destination address +} IpHeaderStruct; + +// Structure of UDP header +typedef struct +{ + unsigned short source; // Source port + unsigned short dest; // Destination port + unsigned short len; // UDP length + unsigned short check; // UDP Checksum +} UdpHeaderStruct; + +#define RTP_PT_PCMU 0 +#define RTP_PT_PCMA 8 + +// Structure of RTP header, only valid for little endian +typedef struct { + unsigned short cc:4; // CSRC count + unsigned short x:1; // header extension flag + unsigned short p:1; // padding flag + unsigned short version:2; // protocol version + unsigned short pt:7; // payload type + unsigned short m:1; // marker bit + unsigned short seq; // sequence number + unsigned int ts; // timestamp + unsigned int ssrc; // synchronization source + //unsigned int csrc[1]; // optional CSRC list +} RtpHeaderStruct; + +#endif + diff --git a/orkaudio/audiocaptureplugins/voip/Rtp.cpp b/orkaudio/audiocaptureplugins/voip/Rtp.cpp new file mode 100644 index 0000000..931aed7 --- /dev/null +++ b/orkaudio/audiocaptureplugins/voip/Rtp.cpp @@ -0,0 +1,257 @@ +/* + * Oreka -- A media capture and retrieval platform + * + * Copyright (C) 2005, orecx LLC + * + * http://www.orecx.com + * + * This program is free software, distributed under the terms of + * the GNU General Public License. + * Please refer to http://www.gnu.org/copyleft/gpl.html + * + */ + +#define _WINSOCKAPI_ // prevents the inclusion of winsock.h + +#include "Utils.h" +#include "Rtp.h" +#include "AudioCapturePlugin.h" +#include "AudioCapturePluginCommon.h" +#include "PacketHeaderDefs.h" +#include "assert.h" + +extern "C" +{ +#include "g711.h" +} +extern AudioChunkCallBackFunction g_audioChunkCallBack; + + +RtpRingBuffer::RtpRingBuffer() +{ + m_writePtr = m_buffer; + m_readPtr = m_buffer; + m_bufferEnd = m_buffer + NUM_SAMPLES_CIRCULAR_BUFFER; + m_writeTimestamp = 0; + m_readTimestamp = 0; + m_log = Logger::getLogger("rtpringbuffer"); + m_shippedSamples = 0; +} + + +void RtpRingBuffer::AddRtpPacket(RtpPacketInfoRef& rtpInfo) +{ + unsigned int rtpEndTimestamp = rtpInfo->m_timestamp + rtpInfo->m_payloadSize; + + if(m_writeTimestamp == 0) + { + // First RTP packet of the session + LOG4CXX_DEBUG(m_log, m_capturePort + " first packet"); + m_writeTimestamp = rtpInfo->m_timestamp; + m_readTimestamp = m_writeTimestamp; + StoreRtpPacket(rtpInfo); + } + else if (rtpInfo->m_timestamp >= m_readTimestamp) // drop packets that are older than last shipment + { + if( (int)(rtpEndTimestamp - m_writeTimestamp) <= (int)FreeSpace()) + { + // RTP packet fits into current buffer + StoreRtpPacket(rtpInfo); + + if(UsedSpace() > NUM_SAMPLES_TRIGGER) + { + // We have enough stuff, make a shipment + CreateShipment(0); + } + } + else + { + // RTP packet does not fit into current buffer + // work out how much silence we need to add to the current buffer when shipping + size_t silenceSize = rtpInfo->m_timestamp - m_writeTimestamp; + CreateShipment(silenceSize); + + // reset buffer + m_writePtr = m_buffer; + m_readPtr = m_buffer; + m_writeTimestamp = rtpInfo->m_timestamp; + m_readTimestamp = m_writeTimestamp; + + // Store new packet + StoreRtpPacket(rtpInfo); + } + } + else + { + LOG4CXX_DEBUG(m_log, m_capturePort + " packet too old, dropped"); + } + CStdString debug; + debug.Format("free:%u used:%u wr:%x rd:%x wrts:%u rdts:%d", FreeSpace(), UsedSpace(), m_writePtr, m_readPtr, m_writeTimestamp, m_readTimestamp); + LOG4CXX_DEBUG(m_log, debug); +} + +// Writes to the internal buffer without any size verification +void RtpRingBuffer::StoreRtpPacket(RtpPacketInfoRef& rtpInfo) +{ + CStdString debug; + + // 1. Silence from write pointer until end of RTP packet + unsigned int endRtpTimestamp = rtpInfo->m_timestamp + rtpInfo->m_payloadSize; + if (endRtpTimestamp > m_writeTimestamp) + { + for(int i=0; i<(endRtpTimestamp - m_writeTimestamp); i++) + { + *m_writePtr = 0; + m_writePtr++; + if(m_writePtr >= m_bufferEnd) + { + m_writePtr = m_buffer; + } + } + int silenceSize = endRtpTimestamp - m_writeTimestamp; + m_writeTimestamp = endRtpTimestamp; + debug.Format("Zeroed %d samples, wr:%x wrts:%u", silenceSize, m_writePtr, m_writeTimestamp); + LOG4CXX_DEBUG(m_log, debug); + } + + // 2. Mix in the latest samples from this RTP packet + unsigned int timestampDelta = m_writeTimestamp - rtpInfo->m_timestamp; + ASSERT(timestampDelta>=0); + short* tempWritePtr = CicularPointerSubtractOffset(m_writePtr, timestampDelta); + unsigned char* payload = rtpInfo->m_payload; + + for(int i=0; im_payloadSize ; i++) + { + if(rtpInfo->m_payloadType == RTP_PT_PCMA) + { + *tempWritePtr += (short)alaw2linear(payload[i]); + } + else if(rtpInfo->m_payloadType == RTP_PT_PCMU) + { + *tempWritePtr += (short)ulaw2linear(payload[i]); + } + tempWritePtr++; + if(tempWritePtr >= m_bufferEnd) + { + tempWritePtr = m_buffer; + } + } + debug.Format("Copied %d samples, tmpwr:%x", rtpInfo->m_payloadSize, tempWritePtr); + LOG4CXX_DEBUG(m_log, debug); +} + +short* RtpRingBuffer::CircularPointerAddOffset(short *ptr, size_t offset) +{ + if((ptr + offset) >= m_bufferEnd) + { + return m_buffer + offset - (m_bufferEnd-ptr); + } + else + { + return ptr + offset; + } +} + +short* RtpRingBuffer::CicularPointerSubtractOffset(short *ptr, size_t offset) +{ + if((ptr-offset) < m_buffer) + { + return m_bufferEnd - offset + (ptr-m_buffer); + } + else + { + return ptr - offset; + } +} + +void RtpRingBuffer::CreateShipment(size_t silenceSize) +{ + // 1. ship from readPtr until stop pointer or until end of buffer if wrapped + bool bufferWrapped = false; + short* stopPtr = NULL; + short* wrappedStopPtr = NULL; + if (silenceSize) + { + // There is additional silence to ship, do not take holdoff into account + stopPtr = m_writePtr; + } + else + { + stopPtr = CicularPointerSubtractOffset(m_writePtr, NUM_SAMPLES_SHIPMENT_HOLDOFF); + } + + if (stopPtr < m_readPtr) + { + wrappedStopPtr = stopPtr; + stopPtr = m_bufferEnd; + bufferWrapped = true; + } + size_t shortSize = stopPtr-m_readPtr; + size_t byteSize = shortSize*2; + AudioChunkRef chunk(new AudioChunk()); + chunk->SetBuffer((void*)m_readPtr, byteSize, AudioChunk::PcmAudio); + g_audioChunkCallBack(chunk, m_capturePort); + m_shippedSamples += shortSize; + m_readPtr = CircularPointerAddOffset(m_readPtr ,shortSize); + m_readTimestamp += shortSize; + + CStdString debug; + debug.Format("Ship %d samples, rd:%x rdts:%u", shortSize, m_readPtr, m_readTimestamp); + LOG4CXX_DEBUG(m_log, debug); + + + // 2. ship from beginning of buffer until stop ptr + if(bufferWrapped) + { + shortSize = wrappedStopPtr - m_buffer; + byteSize = shortSize*2; + chunk.reset(new AudioChunk()); + chunk->SetBuffer((void*)m_buffer, byteSize, AudioChunk::PcmAudio); + g_audioChunkCallBack(chunk, m_capturePort); + m_shippedSamples += shortSize; + m_readPtr = CircularPointerAddOffset(m_readPtr ,shortSize); + m_readTimestamp += shortSize; + debug.Format("Ship wrapped %d samples, rd:%x rdts:%u", shortSize, m_readPtr, m_readTimestamp); + LOG4CXX_DEBUG(m_log, debug); + } + + // 3. ship silence + if (silenceSize) + { + byteSize = silenceSize*2; + char* silenceBuffer = (char*)calloc(byteSize, 1); + if (silenceBuffer) + { + AudioChunkRef chunk(new AudioChunk()); + chunk->SetBuffer((void*)silenceBuffer, byteSize, AudioChunk::PcmAudio); + g_audioChunkCallBack(chunk, m_capturePort); + m_shippedSamples += silenceSize; + m_readPtr = CircularPointerAddOffset(m_readPtr ,silenceSize); + m_readTimestamp += silenceSize; + } + debug.Format("Ship %d silence samples, rd:%x rdts:%u", silenceSize, m_readPtr, m_readTimestamp); + LOG4CXX_DEBUG(m_log, debug); + } +} + + +unsigned int RtpRingBuffer::UsedSpace() +{ + if(m_writePtr >= m_readPtr) + { + return m_writePtr - m_readPtr; + } + return NUM_SAMPLES_CIRCULAR_BUFFER + m_writePtr - m_readPtr; +} + + +unsigned int RtpRingBuffer::FreeSpace() +{ + return NUM_SAMPLES_CIRCULAR_BUFFER - UsedSpace(); +} + +void RtpRingBuffer::SetCapturePort(CStdString& port) +{ + m_capturePort = port; +} + diff --git a/orkaudio/audiocaptureplugins/voip/Rtp.h b/orkaudio/audiocaptureplugins/voip/Rtp.h new file mode 100644 index 0000000..0366ef4 --- /dev/null +++ b/orkaudio/audiocaptureplugins/voip/Rtp.h @@ -0,0 +1,77 @@ +/* + * Oreka -- A media capture and retrieval platform + * + * Copyright (C) 2005, orecx LLC + * + * http://www.orecx.com + * + * This program is free software, distributed under the terms of + * the GNU General Public License. + * Please refer to http://www.gnu.org/copyleft/gpl.html + * + */ + +#ifndef __RTP_H__ +#define __RTP_H__ + +#include "AudioCapture.h" +#include "boost/shared_ptr.hpp" +#include "StdString.h" +#include "LogManager.h" + +#define NUM_SAMPLES_CIRCULAR_BUFFER 8000 +#define NUM_SAMPLES_TRIGGER 4000 // when we have this number of available samples make a shipment +#define NUM_SAMPLES_SHIPMENT_HOLDOFF 2000 // when shipping, ship everything but this number of samples + + +// useful info we extract from an RTP packet +class RtpPacketInfo +{ +public: + //CStdString m_sourceMac; + //CStdString m_destMac; + struct in_addr m_sourceIp; + struct in_addr m_destIp; + unsigned short m_sourcePort; + unsigned short m_destPort; + unsigned int m_payloadSize; + unsigned short m_payloadType; + unsigned char* m_payload; + unsigned short m_seqNum; + unsigned int m_timestamp; +}; +typedef boost::shared_ptr RtpPacketInfoRef; + + +// Ring buffer based on RTP timestamps +// Gathers both sides of the conversation and mixes them into single channel +// Robust to silence suppression +// Supports RTP buffers of arbitrary sizes in both directions. +class RtpRingBuffer +{ +public: + RtpRingBuffer(); + void AddRtpPacket(RtpPacketInfoRef&); + void SetCapturePort(CStdString& port); +private: + void StoreRtpPacket(RtpPacketInfoRef&); + void CreateShipment(size_t silenceSize); + unsigned int FreeSpace(); + unsigned int UsedSpace(); + short* GetHoldOffPtr(); + short* CircularPointerAddOffset(short *ptr, size_t offset); + short* CicularPointerSubtractOffset(short *ptr, size_t offset); + + short* m_writePtr; // pointer after newest RTP data we've received + short* m_readPtr; // where to read from next + unsigned int m_readTimestamp; // timestamp that the next shipment will have + unsigned int m_writeTimestamp; // timestamp that the next RTP buffer should have + short* m_bufferEnd; + short m_buffer[NUM_SAMPLES_CIRCULAR_BUFFER]; + CStdString m_capturePort; + LoggerPtr m_log; + unsigned int m_shippedSamples; +}; + +#endif + diff --git a/orkaudio/audiocaptureplugins/voip/SipSession.cpp b/orkaudio/audiocaptureplugins/voip/SipSession.cpp new file mode 100644 index 0000000..e62a612 --- /dev/null +++ b/orkaudio/audiocaptureplugins/voip/SipSession.cpp @@ -0,0 +1,282 @@ +/* + * Oreka -- A media capture and retrieval platform + * + * Copyright (C) 2005, orecx LLC + * + * http://www.orecx.com + * + * This program is free software, distributed under the terms of + * the GNU General Public License. + * Please refer to http://www.gnu.org/copyleft/gpl.html + * + */ + +#define _WINSOCKAPI_ // prevents the inclusion of winsock.h + +#include "Utils.h" +#include "SipSession.h" +#include "AudioCapturePlugin.h" +#include "AudioCapturePluginCommon.h" +#include +#include "VoIpConfig.h" +#include "ace/OS_NS_arpa_inet.h" + +extern AudioChunkCallBackFunction g_audioChunkCallBack; +extern CaptureEventCallBackFunction g_captureEventCallBack; + +extern VoIpConfigTopObjectRef g_VoIpConfigTopObjectRef; +#define DLLCONFIG g_VoIpConfigTopObjectRef.get()->m_config + + +SipSession::SipSession() +{ + m_lastUpdated = time(NULL); + m_log = Logger::getLogger("sipsession"); + m_invitorIp.s_addr = 0; + m_inviteeIp.s_addr = 0; + m_direction = CaptureEvent::DirUnkn; +} + +void SipSession::Stop() +{ + LOG4CXX_DEBUG(m_log, m_ipAndPort + " SIP Session stop"); + CaptureEventRef stopEvent(new CaptureEvent); + stopEvent->m_type = CaptureEvent::EtStop; + stopEvent->m_timestamp = time(NULL); + g_captureEventCallBack(stopEvent, m_ipAndPort); +} + +void SipSession::Start() +{ + LOG4CXX_DEBUG(m_log, m_ipAndPort + " SIP Session start"); + m_rtpRingBuffer.SetCapturePort(m_ipAndPort); + CaptureEventRef startEvent(new CaptureEvent); + startEvent->m_type = CaptureEvent::EtStart; + startEvent->m_timestamp = time(NULL); + g_captureEventCallBack(startEvent, m_ipAndPort); +} + +void SipSession::ProcessMetadataIncoming() +{ + m_remoteParty = m_invite->m_from; + m_localParty = m_invite->m_to; + m_direction = CaptureEvent::DirIn; +} + +void SipSession::ProcessMetadataOutgoing() +{ + m_remoteParty = m_invite->m_to; + m_localParty = m_invite->m_from; + m_direction = CaptureEvent::DirOut; +} + +void SipSession::ProcessMetadata(RtpPacketInfoRef& rtpPacket) +{ + bool done = false; + + // work out invitee IP address + if(rtpPacket->m_sourceIp.s_addr == m_invitorIp.s_addr) + { + m_inviteeIp = rtpPacket->m_destIp; + } + else if(rtpPacket->m_destIp.s_addr == m_invitorIp.s_addr) + { + m_inviteeIp = rtpPacket->m_sourceIp; + } + else + { + LOG4CXX_DEBUG(m_log, m_ipAndPort + " alien RTP packet"); + } + + // work out capture port and direction + if(DLLCONFIG.IsMediaGateway(m_invitorIp)) + { + if(DLLCONFIG.IsMediaGateway(m_inviteeIp)) + { + // dismiss + } + if(DLLCONFIG.IsPartOfLan(m_inviteeIp)) + { + ProcessMetadataIncoming(); + } + } + else if (DLLCONFIG.IsPartOfLan(m_invitorIp)) + { + ProcessMetadataOutgoing(); + } + else + { + // SIP invitor IP address is an outside IP address + if(DLLCONFIG.IsMediaGateway(m_inviteeIp)) + { + // dismiss + } + else if(DLLCONFIG.IsPartOfLan(m_inviteeIp)) + { + ProcessMetadataIncoming(); + } + else + { + // SIP invitee IP address is an outside IP address + ProcessMetadataOutgoing(); + } + } + ReportMetadata(); +} + +void SipSession::ReportMetadata() +{ + // report Local party + CaptureEventRef event(new CaptureEvent()); + event->m_type = CaptureEvent::EtLocalParty; + event->m_value = m_localParty; + g_captureEventCallBack(event, m_ipAndPort); + + // Report remote party + event.reset(new CaptureEvent()); + event->m_type = CaptureEvent::EtRemoteParty; + event->m_value = m_remoteParty; + g_captureEventCallBack(event, m_ipAndPort); + + // report direction + event.reset(new CaptureEvent()); + event->m_type = CaptureEvent::EtDirection; + event->m_value = CaptureEvent::DirectionToString(m_direction); + g_captureEventCallBack(event, m_ipAndPort); +} + + +void SipSession::AddRtpPacket(RtpPacketInfoRef& rtpPacket) +{ + // if first RTP packet, start session + if(m_lastRtpPacket.get() == NULL) + { + Start(); + ProcessMetadata(rtpPacket); + } + m_lastRtpPacket = rtpPacket; + + // send audio buffer + //AudioChunkRef chunkRef(new AudioChunk); + //chunkRef->SetBuffer(rtpPacket->m_payload, rtpPacket->m_payloadSize, AudioChunk::AlawAudio); + //g_audioChunkCallBack(chunkRef, m_ipAndPort); + + m_rtpRingBuffer.AddRtpPacket(rtpPacket); + + m_lastUpdated = time(NULL); +} + + +void SipSession::ReportSipInvite(SipInviteInfoRef& invite) +{ + m_invite = invite; + m_invitorIp = invite->m_fromIp; +} + +//===================================================================== +SipSessions::SipSessions() +{ + m_log = Logger::getLogger("sipsessions"); +} + + +void SipSessions::ReportSipInvite(SipInviteInfoRef& invite) +{ + CStdString key = CStdString(ACE_OS::inet_ntoa(invite->m_fromIp)) + "," + invite->m_fromRtpPort; + std::map::iterator pair; + + pair = m_byIpAndPort.find(key); + + if (pair != m_byIpAndPort.end()) + { + // A session exists ont the same IP+port, stop old session + SipSessionRef session = pair->second; + Stop(session); + } + // create new session and insert into both maps + SipSessionRef session(new SipSession()); + session->m_ipAndPort = key; + session->ReportSipInvite(invite); + m_byCallId.insert(std::make_pair(invite->m_callId, session)); + m_byIpAndPort.insert(std::make_pair(key, session)); +} + +void SipSessions::ReportSipBye(SipByeInfo bye) +{ + std::map::iterator pair; + pair = m_byCallId.find(bye.m_callId); + + if (pair != m_byCallId.end()) + { + // Session found: stop it + SipSessionRef session = pair->second; + Stop(session); + } +} + +void SipSessions::Stop(SipSessionRef& session) +{ + session->Stop(); + m_byIpAndPort.erase(session->m_ipAndPort); + m_byCallId.erase(session->m_invite->m_callId); +} + + +void SipSessions::ReportRtpPacket(RtpPacketInfoRef& rtpPacket) +{ + // Does a session exist with this source Ip+Port + SipSessionRef session; + CStdString port = IntToString(rtpPacket->m_sourcePort); + CStdString ipAndPort = CStdString(ACE_OS::inet_ntoa(rtpPacket->m_sourceIp)) + "," + port; + std::map::iterator pair; + + pair = m_byIpAndPort.find(ipAndPort); + if (pair != m_byIpAndPort.end()) + { + session = pair->second; + } + else + { + // Does a session exist with this destination Ip+Port + port = IntToString(rtpPacket->m_destPort); + ipAndPort = CStdString(ACE_OS::inet_ntoa(rtpPacket->m_destIp)) + "," + port; + pair = m_byIpAndPort.find(ipAndPort); + if (pair != m_byIpAndPort.end()) + { + session = pair->second; + } + } + if (!session.get() == NULL) + { + // Found a session give it the RTP packet info + session->AddRtpPacket(rtpPacket); + } +} + +void SipSessions::Hoover(time_t now) +{ + CStdString numSessions = IntToString(m_byIpAndPort.size()); + LOG4CXX_DEBUG(m_log, "Hoover - check " + numSessions + " sessions time:" + IntToString(now)); + + // Go round the sessions and find inactive ones + std::map::iterator pair; + std::list toDismiss; + + for(pair = m_byIpAndPort.begin(); pair != m_byIpAndPort.end(); pair++) + { + SipSessionRef session = pair->second; + if((now - session->m_lastUpdated) > 10) + { + toDismiss.push_back(session); + } + } + + // discard inactive sessions + for (std::list::iterator it = toDismiss.begin(); it != toDismiss.end() ; it++) + { + SipSessionRef session = *it; + LOG4CXX_DEBUG(m_log, session->m_ipAndPort + " Expired"); + Stop(session); + } +} + diff --git a/orkaudio/audiocaptureplugins/voip/SipSession.h b/orkaudio/audiocaptureplugins/voip/SipSession.h new file mode 100644 index 0000000..4702bac --- /dev/null +++ b/orkaudio/audiocaptureplugins/voip/SipSession.h @@ -0,0 +1,87 @@ +/* + * Oreka -- A media capture and retrieval platform + * + * Copyright (C) 2005, orecx LLC + * + * http://www.orecx.com + * + * This program is free software, distributed under the terms of + * the GNU General Public License. + * Please refer to http://www.gnu.org/copyleft/gpl.html + * + */ + +#ifndef __SIPSESSION_H__ +#define __SIPSESSION_H__ + +#include "SipSession.h" +#include "Rtp.h" +#include +#include "ace/Singleton.h" + +class SipInviteInfo +{ +public: + struct in_addr m_fromIp; + CStdString m_fromRtpPort; + CStdString m_from; + CStdString m_to; + CStdString m_callId; +}; +typedef boost::shared_ptr SipInviteInfoRef; + +class SipByeInfo +{ +public: + CStdString m_callId; +}; + + +class SipSession +{ +public: + SipSession(); + void Stop(); + void Start(); + void AddRtpPacket(RtpPacketInfoRef& rtpPacket); + void ReportSipInvite(SipInviteInfoRef& invite); + + CStdString m_ipAndPort; + SipInviteInfoRef m_invite; + time_t m_lastUpdated; +private: + void ProcessMetadata(RtpPacketInfoRef&); + void ProcessMetadataIncoming(); + void ProcessMetadataOutgoing(); + void ReportMetadata(); + + RtpPacketInfoRef m_lastRtpPacket; + RtpRingBuffer m_rtpRingBuffer; + struct in_addr m_invitorIp; + struct in_addr m_inviteeIp; + LoggerPtr m_log; + CStdString m_capturePort; + CStdString m_localParty; + CStdString m_remoteParty; + CaptureEvent::DirectionEnum m_direction; +}; +typedef boost::shared_ptr SipSessionRef; + +class SipSessions +{ +public: + SipSessions(); + void Stop(SipSessionRef& session); + void ReportSipInvite(SipInviteInfoRef& invite); + void ReportSipBye(SipByeInfo bye); + void ReportRtpPacket(RtpPacketInfoRef& rtpPacket); + void Hoover(time_t now); +private: + std::map m_byIpAndPort; + std::map m_byCallId; + LoggerPtr m_log; +}; +typedef ACE_Singleton SipSessionsSingleton; + +#endif + diff --git a/orkaudio/audiocaptureplugins/voip/VoIp.cpp b/orkaudio/audiocaptureplugins/voip/VoIp.cpp new file mode 100644 index 0000000..e7a3b6d --- /dev/null +++ b/orkaudio/audiocaptureplugins/voip/VoIp.cpp @@ -0,0 +1,465 @@ +/* + * Oreka -- A media capture and retrieval platform + * + * Copyright (C) 2005, orecx LLC + * + * http://www.orecx.com + * + * This program is free software, distributed under the terms of + * the GNU General Public License. + * Please refer to http://www.gnu.org/copyleft/gpl.html + * + */ + +#define _WINSOCKAPI_ // prevents the inclusion of winsock.h + +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_string.h" +#include "ace/Singleton.h" +#include "ace/Min_Max.h" +#include "ace/OS_NS_arpa_inet.h" +#include "ace/OS_NS_ctype.h" +#include "AudioCapturePlugin.h" +#include "AudioCapturePluginCommon.h" +#include "Utils.h" +#include "VoIpConfig.h" +#include "pcap.h" +#include "PacketHeaderDefs.h" +#include "Rtp.h" +#include "SipSession.h" + +extern AudioChunkCallBackFunction g_audioChunkCallBack; +extern CaptureEventCallBackFunction g_captureEventCallBack; +extern LogManager* g_logManager; + +#include "LogManager.h" + +static LoggerPtr s_log; +static LoggerPtr s_sipExtractionLog; +time_t lastHooveringTime; + +VoIpConfigTopObjectRef g_VoIpConfigTopObjectRef; +#define DLLCONFIG g_VoIpConfigTopObjectRef.get()->m_config + +// find the address that follows the given search string between start and stop pointers +char* memFindAfter(char* toFind, char* start, char* stop) +{ + for(char * ptr = start; (ptrversion == 2) + { + u_short source = ntohs(udpHeader->source); + u_short dest = ntohs(udpHeader->dest); + if(!(ntohs(udpHeader->source)%2) && !(ntohs(udpHeader->dest)%2)) // udp ports must be even + { + if(rtpHeader->pt == RTP_PT_PCMU || rtpHeader->pt == RTP_PT_PCMA) + { + result = true; + u_char* payload = (u_char *)rtpHeader + sizeof(RtpHeaderStruct); + u_char* packetEnd = (u_char *)ipHeader + ntohs(ipHeader->ip_len); + u_int payloadLength = packetEnd - payload; + + RtpPacketInfoRef rtpInfo(new RtpPacketInfo()); + rtpInfo->m_sourceIp = ipHeader->ip_src; + rtpInfo->m_destIp = ipHeader->ip_dest; + rtpInfo->m_sourcePort = ntohs(udpHeader->source); + rtpInfo->m_destPort = ntohs(udpHeader->dest); + rtpInfo->m_payloadSize = payloadLength; + rtpInfo->m_payloadType = rtpHeader->pt; + rtpInfo->m_seqNum = ntohs(rtpHeader->seq); + rtpInfo->m_timestamp = ntohl(rtpHeader->ts); + rtpInfo->m_payload = payload; + + CStdString debug; + debug.Format("%s,%d seq:%u ts:%u len:%d", ACE_OS::inet_ntoa(rtpInfo->m_sourceIp), rtpInfo->m_sourcePort, ntohs(rtpHeader->seq), ntohl(rtpHeader->ts), payloadLength); + LOG4CXX_DEBUG(s_log, debug); + + SipSessionsSingleton::instance()->ReportRtpPacket(rtpInfo); + } + } + } + return result; +} + + +bool TrySipBye(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader, UdpHeaderStruct* udpHeader, u_char* udpPayload) +{ + bool result = false; + if (memcmp("BYE", (void*)udpPayload, 1) == 0) + { + result = true; + int sipLength = ntohs(udpHeader->len); + char* sipEnd = (char*)udpPayload + sipLength; + SipByeInfo info; + char* callIdField = memFindAfter("Call-ID: ", (char*)udpPayload, sipEnd); + if(callIdField) + { + GrabToken(callIdField, info.m_callId); + SipSessionsSingleton::instance()->ReportSipBye(info); + } + LOG4CXX_DEBUG(s_log, "SIP BYE"); + } + return result; +} + +bool TrySipInvite(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader, UdpHeaderStruct* udpHeader, u_char* udpPayload) +{ + bool result = false; + if (memcmp("INVITE", (void*)udpPayload, 1) == 0) + { + result = true; + + int sipLength = ntohs(udpHeader->len); + char* sipEnd = (char*)udpPayload + sipLength; + + SipInviteInfoRef info(new SipInviteInfo()); + + char* fromField = memFindAfter("From: ", (char*)udpPayload, sipEnd); + char* toField = NULL; + char* callIdField = NULL; + char* audioField = NULL; + + if(fromField) + { + if(s_sipExtractionLog->isDebugEnabled()) + { + CStdString from; + GrabLine(fromField, sipEnd, from); + s_sipExtractionLog->forcedLog(Level::DEBUG, "from: " + from); + } + + char* fromFieldEnd = memFindEOL(fromField, sipEnd); + + char* sipUser = memFindAfter("sip:", fromField, fromFieldEnd); + if(sipUser) + { + GrabAlphaNumToken(sipUser, fromFieldEnd, info->m_from); + } + else + { + GrabAlphaNumToken(fromField, fromFieldEnd, info->m_from); + } + toField = memFindAfter("To: ", fromField, sipEnd); + } + if(toField) + { + char* toFieldEnd = NULL; + if(s_sipExtractionLog->isDebugEnabled()) + { + CStdString to; + toFieldEnd = GrabLine(toField, sipEnd, to); + s_sipExtractionLog->forcedLog(Level::DEBUG, "to: " + to); + } + + char* sipUser = memFindAfter("sip:", toField, toFieldEnd); + if(sipUser) + { + GrabAlphaNumToken(sipUser, toFieldEnd, info->m_to); + } + else + { + GrabAlphaNumToken(toField, toFieldEnd, info->m_to); + } + callIdField = memFindAfter("Call-ID: ", toField, sipEnd); + } + if(callIdField) + { + GrabToken(callIdField, info->m_callId); + audioField = memFindAfter("m=audio ", callIdField, sipEnd); + } + if(audioField) + { + GrabToken(audioField, info->m_fromRtpPort); + info->m_fromIp = ipHeader->ip_src; + SipSessionsSingleton::instance()->ReportSipInvite(info); + } + LOG4CXX_DEBUG(s_log, "SIP INVITE"); + } + return result; +} + +void HandlePacket(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data) +{ + EthernetHeaderStruct* ethernetHeader = (EthernetHeaderStruct *)pkt_data; + IpHeaderStruct* ipHeader = (IpHeaderStruct*)((char*)ethernetHeader + sizeof(EthernetHeaderStruct)); + int ipHeaderLength = ipHeader->ip_hl*4; + + //CStdString source = ACE_OS::inet_ntoa(ipHeader->ip_src); + //CStdString dest = ACE_OS::inet_ntoa(ipHeader->ip_dest); + //CStdString debug; + //debug.Format("%x, %x", ethernetHeader, ipHeader); + //LOG4CXX_INFO(s_log, source + "-" + dest); + + //LOG4CXX_DEBUG(s_log, "*"); + + if(ipHeader->ip_p == IPPROTO_UDP) + { + UdpHeaderStruct* udpHeader = (UdpHeaderStruct*)((char *)ipHeader + ipHeaderLength); + + if( ntohs(udpHeader->source) > 5000 && ntohs(udpHeader->dest) > 5000 ) + { + u_char* udpPayload = (u_char *)udpHeader + sizeof(UdpHeaderStruct); + + + bool detectedUsefulPacket = TryRtp(ethernetHeader, ipHeader, udpHeader, udpPayload); + + if(!detectedUsefulPacket) + { + detectedUsefulPacket= TrySipInvite(ethernetHeader, ipHeader, udpHeader, udpPayload); + } + if(!detectedUsefulPacket) + { + detectedUsefulPacket = TrySipBye(ethernetHeader, ipHeader, udpHeader, udpPayload); + } + } + } + + time_t now = time(NULL); + if((now - lastHooveringTime) > 5) + { + lastHooveringTime = now; + SipSessionsSingleton::instance()->Hoover(now); + } +} + + +class VoIp +{ +public: + VoIp(); + void Initialize(); + void Run(); + void StartCapture(CStdString& port); + void StopCapture(CStdString& port); +private: + pcap_t* m_pcapHandle; +}; + +typedef ACE_Singleton VoIpSingleton; + +VoIp::VoIp() +{ + m_pcapHandle = NULL; +} + +void Configure(DOMNode* node) +{ + if (node) + { + VoIpConfigTopObjectRef VoIpConfigTopObjectRef(new VoIpConfigTopObject); + try + { + VoIpConfigTopObjectRef.get()->DeSerializeDom(node); + g_VoIpConfigTopObjectRef = VoIpConfigTopObjectRef; + } + catch (CStdString& e) + { + LOG4CXX_WARN(g_logManager->rootLog, "VoIp.dll: " + e); + } + } + else + { + LOG4CXX_WARN(g_logManager->rootLog, "VoIp.dll: got empty DOM tree"); + } +} + + +void VoIp::Initialize() +{ + s_log = Logger::getLogger("voip"); + s_sipExtractionLog = Logger::getLogger("sipextraction"); + LOG4CXX_INFO(s_log, "Initializing VoIP plugin"); + + // create a default config object in case it was not properly initialized by Configure + if(!g_VoIpConfigTopObjectRef.get()) + { + g_VoIpConfigTopObjectRef.reset(new VoIpConfigTopObject); + } + +/* + char addr1[] = "10.1.2.3"; + char addr2[] = "192.168.5.6"; + char addr3[] = "45.168.5.6"; + struct in_addr addr; + ACE_OS::inet_aton(addr1, &addr); + bool result = DLLCONFIG.IsPartOfLan(addr); + result = DLLCONFIG.IsMediaGateway(addr); + ACE_OS::inet_aton(addr2, &addr); + result = DLLCONFIG.IsPartOfLan(addr); + result = DLLCONFIG.IsMediaGateway(addr); + ACE_OS::inet_aton(addr3, &addr); + result = DLLCONFIG.IsPartOfLan(addr); + result = DLLCONFIG.IsMediaGateway(addr); +*/ + + pcap_if_t* devices = NULL; + pcap_if_t* deviceToSniff = NULL; + lastHooveringTime = time(NULL); + + char * error = NULL; + if (pcap_findalldevs(&devices, error) == -1) + { + LOG4CXX_ERROR(s_log, CStdString("pcap error when discovering devices: ") + error); + } + else + { + if(devices) + { + LOG4CXX_INFO(s_log, CStdString("Available pcap devices:")); + + for (pcap_if_t* device = devices; device != NULL; device = device->next) + { + LOG4CXX_INFO(s_log, CStdString("\t* ") + device->description + " " + device->name ); + if(DLLCONFIG.m_device.Equals(device->name)) + { + deviceToSniff = device; + } + } + if (!deviceToSniff) + { + LOG4CXX_ERROR(s_log, CStdString("pcap could not find wanted device: ") + DLLCONFIG.m_device); + } + else + { + #define PROMISCUOUS 1 + if ((m_pcapHandle = pcap_open_live(deviceToSniff->name, 1500, PROMISCUOUS, + 500, error)) == NULL) + { + LOG4CXX_ERROR(s_log, CStdString("pcap error when opening device: ") + deviceToSniff->name); + } + else + { + LOG4CXX_INFO(s_log, CStdString("successfully opened device: ") + deviceToSniff->name); + + //unsigned int netMask = 0xffffff; + //if(deviceToSniff->addresses != NULL) + //{ + // netMask=((struct sockaddr_in *)(device->addresses->netmask))->sin_addr.s_addr; + //} + + //struct bpf_program program; + //if (pcap_compile(m_pcapHandle, &program, "ip proto icmp", 1, 0xffffffff) < 0) + //{ + // LOG4CXX_ERROR(s_log, CStdString("unable to compile pcap filter")); + //} + } + } + } + else + { + LOG4CXX_ERROR(s_log, CStdString("pcap could not find any device")); + } + } + +} + +void VoIp::Run() +{ + LOG4CXX_INFO(s_log, "Running VoIp.dll"); + pcap_loop(m_pcapHandle, 0, HandlePacket, NULL); +} + +void VoIp::StartCapture(CStdString& port) +{ + ; +} + +void VoIp::StopCapture(CStdString& port) +{ + ; +} + +void __CDECL__ Initialize() +{ + VoIpSingleton::instance()->Initialize(); +} + +void __CDECL__ Run() +{ + VoIpSingleton::instance()->Run(); +} + +void __CDECL__ StartCapture(CStdString& capturePort) +{ + ; +} + +void __CDECL__ StopCapture(CStdString& capturePort) +{ + ; +} + diff --git a/orkaudio/audiocaptureplugins/voip/VoIp.dsp b/orkaudio/audiocaptureplugins/voip/VoIp.dsp new file mode 100644 index 0000000..c58a936 --- /dev/null +++ b/orkaudio/audiocaptureplugins/voip/VoIp.dsp @@ -0,0 +1,158 @@ +# Microsoft Developer Studio Project File - Name="VoIp" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=VoIp - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "VoIp.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "VoIp.mak" CFG="VoIp - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "VoIp - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "VoIp - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "VoIp - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VoIp_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "C:\devExt\winpcap\WpdPack\Include" /I "../.." /I "../../../OrkBaseCxx" /I "C:\devExt\boost\boost_1_32_0" /I "C:\devExt\ACE_wrappers" /I "C:\devExt\xerces++\xerces-c_2_6_0-windows_nt-msvc_60\include" /I "../Common" /I "C:\devExt\log4cxx\log4cxx-0.9.7\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VoIp_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 packet.lib wpcap.lib wininet.lib OrkBase.lib ace.lib log4cxx.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /libpath:"C:\devExt\winpcap\WpdPack\Lib" /libpath:"../../../OrkBaseCxx/Release" /libpath:"C:\devExt\log4cxx\log4cxx-0.9.7\msvc\Lib\Release" /libpath:"C:\devExt\ACE_wrappers\lib" + +!ELSEIF "$(CFG)" == "VoIp - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VoIp_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "C:\devExt\winpcap\WpdPack\Include" /I "../.." /I "../../../OrkBaseCxx" /I "C:\devExt\boost\boost_1_32_0" /I "C:\devExt\ACE_wrappers" /I "C:\devExt\xerces++\xerces-c_2_6_0-windows_nt-msvc_60\include" /I "../Common" /I "C:\devExt\log4cxx\log4cxx-0.9.7\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VoIp_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 packet.lib wpcap.lib wininet.lib OrkBaseD.lib aced.lib log4cxx.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept /libpath:"C:\devExt\winpcap\WpdPack\Lib" /libpath:"../../../OrkBaseCxx/Debug" /libpath:"C:\devExt\log4cxx\log4cxx-0.9.7\msvc\Lib\Debug" /libpath:"C:\devExt\ACE_wrappers\lib" + +!ENDIF + +# Begin Target + +# Name "VoIp - Win32 Release" +# Name "VoIp - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\AudioCapturePlugin.h +# End Source File +# Begin Source File + +SOURCE=..\Common\AudioCapturePluginCommon.cpp +# End Source File +# Begin Source File + +SOURCE=..\Common\AudioCapturePluginCommon.h +# End Source File +# Begin Source File + +SOURCE=..\..\ConfigManager.h +# End Source File +# Begin Source File + +SOURCE=.\g711.c +# End Source File +# Begin Source File + +SOURCE=.\g711.h +# End Source File +# Begin Source File + +SOURCE=.\PacketHeaderDefs.h +# End Source File +# Begin Source File + +SOURCE=.\Rtp.cpp +# End Source File +# Begin Source File + +SOURCE=.\Rtp.h +# End Source File +# Begin Source File + +SOURCE=.\SipSession.cpp +# End Source File +# Begin Source File + +SOURCE=.\SipSession.h +# End Source File +# Begin Source File + +SOURCE=.\VoIp.cpp +# End Source File +# Begin Source File + +SOURCE=.\VoIpConfig.cpp +# End Source File +# Begin Source File + +SOURCE=.\VoIpConfig.h +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/orkaudio/audiocaptureplugins/voip/VoIpConfig.cpp b/orkaudio/audiocaptureplugins/voip/VoIpConfig.cpp new file mode 100644 index 0000000..a632bdc --- /dev/null +++ b/orkaudio/audiocaptureplugins/voip/VoIpConfig.cpp @@ -0,0 +1,127 @@ +/* + * Oreka -- A media capture and retrieval platform + * + * Copyright (C) 2005, orecx LLC + * + * http://www.orecx.com + * + * This program is free software, distributed under the terms of + * the GNU General Public License. + * Please refer to http://www.gnu.org/copyleft/gpl.html + * + */ + +#define _WINSOCKAPI_ // prevents the inclusion of winsock.h + +#include "Utils.h" +#include "serializers/Serializer.h" +#include "VoIpConfig.h" +#include "ace/OS_NS_arpa_inet.h" + +VoIpConfig::VoIpConfig() +{ + // Standard LAN internal IP range masks + m_asciiLanMasks.push_back("192.168.255.255"); + m_asciiLanMasks.push_back("10.255.255.255"); + m_asciiLanMasks.push_back("172.31.255.255"); +} + +void VoIpConfig::Define(Serializer* s) +{ + s->StringValue(DEVICE_PARAM, m_device); + s->CsvValue("LanMasks", m_asciiLanMasks); + s->CsvValue("MediaGateways", m_asciiMediaGateways); +} + +void VoIpConfig::Validate() +{ + // iterate over ascii LAN masks and populate the binary LAN Masks list + m_lanMasks.clear(); + std::list::iterator it; + for(it = m_asciiLanMasks.begin(); it != m_asciiLanMasks.end(); it++) + { + struct in_addr a; + if(ACE_OS::inet_aton((PCSTR)*it, &a)) + { + m_lanMasks.push_back((unsigned int)a.s_addr); + } + else + { + throw (CStdString("VoIpConfig: invalid IP address in LanMasks:" + *it)); + } + } + + // iterate over ascii Media gateway IP addresses and populate the binary Media gateway IP addresses list + m_mediaGateways.clear(); + for(it = m_asciiMediaGateways.begin(); it != m_asciiMediaGateways.end(); it++) + { + struct in_addr a; + if(ACE_OS::inet_aton((PCSTR)*it, &a)) + { + m_mediaGateways.push_back((unsigned int)a.s_addr); + } + else + { + throw (CStdString("VoIpConfig: invalid IP address in MediaGateways:" + *it)); + } + } +} + +bool VoIpConfig::IsPartOfLan(struct in_addr addr) +{ + for(std::list::iterator it = m_lanMasks.begin(); it != m_lanMasks.end(); it++) + { + if(((unsigned int)addr.s_addr & *it) == (unsigned int)addr.s_addr) + { + return true; + } + } + return false; +} + +bool VoIpConfig::IsMediaGateway(struct in_addr addr) +{ + for(std::list::iterator it = m_mediaGateways.begin(); it != m_mediaGateways.end(); it++) + { + if((unsigned int)addr.s_addr == *it) + { + return true; + } + } + return false; +} + + +CStdString VoIpConfig::GetClassName() +{ + return CStdString("VoIpConfig"); +} + +ObjectRef VoIpConfig::NewInstance() +{ + return ObjectRef(new VoIpConfig); +} + +//==================================== + + +void VoIpConfigTopObject::Define(Serializer* s) +{ + s->ObjectValue(SOUND_DEVICE_CONFIG_PARAM, m_config, true); +} + +void VoIpConfigTopObject::Validate() +{ + ; +} + +CStdString VoIpConfigTopObject::GetClassName() +{ + return CStdString("VoIpConfigTopObject"); +} + +ObjectRef VoIpConfigTopObject::NewInstance() +{ + return ObjectRef(new VoIpConfigTopObject); +} + diff --git a/orkaudio/audiocaptureplugins/voip/VoIpConfig.h b/orkaudio/audiocaptureplugins/voip/VoIpConfig.h new file mode 100644 index 0000000..7bea647 --- /dev/null +++ b/orkaudio/audiocaptureplugins/voip/VoIpConfig.h @@ -0,0 +1,68 @@ +/* + * Oreka -- A media capture and retrieval platform + * + * Copyright (C) 2005, orecx LLC + * + * http://www.orecx.com + * + * This program is free software, distributed under the terms of + * the GNU General Public License. + * Please refer to http://www.gnu.org/copyleft/gpl.html + * + */ + +#ifndef __VOIPCONFIG_H__ +#define __VOIPCONFIG_H__ + +#include +#include "StdString.h" +#include "Object.h" +#include "boost/shared_ptr.hpp" + +#define DEVICE_PARAM "Device" + +/** This class defines various configuration parameters for the generator. */ +class VoIpConfig : public Object +{ +public: + VoIpConfig(); + void Define(Serializer* s); + void Validate(); + + CStdString GetClassName(); + ObjectRef NewInstance(); + inline ObjectRef Process() {return ObjectRef();}; + + bool IsPartOfLan(struct in_addr); + bool IsMediaGateway(struct in_addr); + + CStdString m_device; + std::list m_mediaGateways; + std::list m_asciiMediaGateways; + std::list m_lanMasks; + std::list m_asciiLanMasks; +}; + +//======================================== + +#define SOUND_DEVICE_CONFIG_PARAM "VoIpPlugin" + +/** This class represents the top of the configuration hierarchy for the generator. */ +class VoIpConfigTopObject : public Object +{ +public: + void Define(Serializer* s); + void Validate(); + + CStdString GetClassName(); + ObjectRef NewInstance(); + inline ObjectRef Process() {return ObjectRef();}; + + VoIpConfig m_config; +}; + +typedef boost::shared_ptr VoIpConfigTopObjectRef; + + +#endif + diff --git a/orkaudio/audiocaptureplugins/voip/g711.c b/orkaudio/audiocaptureplugins/voip/g711.c new file mode 100644 index 0000000..7317dbb --- /dev/null +++ b/orkaudio/audiocaptureplugins/voip/g711.c @@ -0,0 +1,285 @@ +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#include "g711.h" + +/* + * g711.c + * + * u-law, A-law and linear PCM conversions. + */ +#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ +#define QUANT_MASK (0xf) /* Quantization field mask. */ +#define NSEGS (8) /* Number of A-law segments. */ +#define SEG_SHIFT (4) /* Left shift for segment number. */ +#define SEG_MASK (0x70) /* Segment field mask. */ + +static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF, + 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF}; + +/* copy from CCITT G.711 specifications */ +unsigned char _u2a[128] = { /* u- to A-law conversions */ + 1, 1, 2, 2, 3, 3, 4, 4, + 5, 5, 6, 6, 7, 7, 8, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 27, 29, 31, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, + 46, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128}; + +unsigned char _a2u[128] = { /* A- to u-law conversions */ + 1, 3, 5, 7, 9, 11, 13, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 32, 33, 33, 34, 34, 35, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 48, 49, 49, + 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 64, + 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}; + +static int +search( + int val, + short *table, + int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (val <= *table++) + return (i); + } + return (size); +} + +/* + * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law + * + * linear2alaw() accepts an 16-bit integer and encodes it as A-law data. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +unsigned char +linear2alaw( + int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char aval; + + if (pcm_val >= 0) { + mask = 0xD5; /* sign (7th) bit = 1 */ + } else { + mask = 0x55; /* sign bit = 0 */ + pcm_val = -pcm_val - 8; + } + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm_val, seg_end, 8); + + /* Combine the sign, segment, and quantization bits. */ + + if (seg >= 8) /* out of range, return maximum value. */ + return (0x7F ^ mask); + else { + aval = seg << SEG_SHIFT; + if (seg < 2) + aval |= (pcm_val >> 4) & QUANT_MASK; + else + aval |= (pcm_val >> (seg + 3)) & QUANT_MASK; + return (aval ^ mask); + } +} + +/* + * alaw2linear() - Convert an A-law value to 16-bit linear PCM + * + */ +int +alaw2linear( + unsigned char a_val) +{ + int t; + int seg; + + a_val ^= 0x55; + + t = (a_val & QUANT_MASK) << 4; + seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; + switch (seg) { + case 0: + t += 8; + break; + case 1: + t += 0x108; + break; + default: + t += 0x108; + t <<= seg - 1; + } + return ((a_val & SIGN_BIT) ? t : -t); +} + +#define BIAS (0x84) /* Bias for linear code. */ + +/* + * linear2ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +unsigned char +linear2ulaw( + int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char uval; + + /* Get the sign and the magnitude of the value. */ + if (pcm_val < 0) { + pcm_val = BIAS - pcm_val; + mask = 0x7F; + } else { + pcm_val += BIAS; + mask = 0xFF; + } + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm_val, seg_end, 8); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + if (seg >= 8) /* out of range, return maximum value. */ + return (0x7F ^ mask); + else { + uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF); + return (uval ^ mask); + } + +} + +/* + * ulaw2linear() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +int +ulaw2linear( + unsigned char u_val) +{ + int t; + + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & QUANT_MASK) << 3) + BIAS; + t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; + + return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); +} + +/* A-law to u-law conversion */ +unsigned char +alaw2ulaw( + unsigned char aval) +{ + aval &= 0xff; + return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) : + (0x7F ^ _a2u[aval ^ 0x55])); +} + +/* u-law to A-law conversion */ +unsigned char +ulaw2alaw( + unsigned char uval) +{ + uval &= 0xff; + return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) : + (0x55 ^ (_u2a[0x7F ^ uval] - 1))); +} diff --git a/orkaudio/audiocaptureplugins/voip/g711.h b/orkaudio/audiocaptureplugins/voip/g711.h new file mode 100644 index 0000000..f15df4d --- /dev/null +++ b/orkaudio/audiocaptureplugins/voip/g711.h @@ -0,0 +1,21 @@ +/* + * Oreka -- A media capture and retrieval platform + * + * Copyright (C) 2005, orecx LLC + * + * http://www.orecx.com + * + * This program is free software, distributed under the terms of + * the GNU General Public License. + * Please refer to http://www.gnu.org/copyleft/gpl.html + * + */ + +#ifndef __G711_H__ +#define __G711_H__ + +int ulaw2linear(unsigned char u_val); +int alaw2linear(unsigned char a_val); + +#endif + -- cgit v1.2.3