diff options
-rw-r--r-- | orkaudio/audiocaptureplugins/voip/PacketHeaderDefs.h | 9 | ||||
-rw-r--r-- | orkaudio/audiocaptureplugins/voip/Rtp.cpp | 4 | ||||
-rw-r--r-- | orkaudio/audiocaptureplugins/voip/Rtp.h | 13 | ||||
-rw-r--r-- | orkaudio/audiocaptureplugins/voip/RtpSession.cpp | 96 | ||||
-rw-r--r-- | orkaudio/audiocaptureplugins/voip/RtpSession.h | 11 | ||||
-rw-r--r-- | orkaudio/audiocaptureplugins/voip/VoIp.cpp | 31 |
6 files changed, 163 insertions, 1 deletions
diff --git a/orkaudio/audiocaptureplugins/voip/PacketHeaderDefs.h b/orkaudio/audiocaptureplugins/voip/PacketHeaderDefs.h index ad6906d..7bf61d9 100644 --- a/orkaudio/audiocaptureplugins/voip/PacketHeaderDefs.h +++ b/orkaudio/audiocaptureplugins/voip/PacketHeaderDefs.h @@ -92,6 +92,15 @@ typedef struct //unsigned int csrc[1]; // optional CSRC list } RtpHeaderStruct; +// Structure of RTP payload format for RTP events (ref RFC 2833, section +// 3.5) +typedef struct +{ + unsigned char event; + unsigned char er_volume; + unsigned short duration; +} RtpEventPayloadFormat; + //=================================================================== // Cisco Callmanager -> endpoint messages typedef struct diff --git a/orkaudio/audiocaptureplugins/voip/Rtp.cpp b/orkaudio/audiocaptureplugins/voip/Rtp.cpp index 5e53f3e..a35e519 100644 --- a/orkaudio/audiocaptureplugins/voip/Rtp.cpp +++ b/orkaudio/audiocaptureplugins/voip/Rtp.cpp @@ -41,3 +41,7 @@ void RtpPacketInfo::ToString(CStdString& string) string.Format("%s,%d %s,%d seq:%u ts:%u len:%d type:%d", sourceIp, m_sourcePort, destIp, m_destPort, m_seqNum, m_timestamp, m_payloadSize, m_payloadType); } +void RtpEventInfo::ToString(CStdString& string) +{ + string.Format("event:%d e:%d r:%d volume:%d duration:%d timestamp:%u", m_event, m_e, m_r, m_volume, m_duration, m_startTimestamp); +} diff --git a/orkaudio/audiocaptureplugins/voip/Rtp.h b/orkaudio/audiocaptureplugins/voip/Rtp.h index 0e50979..b1568b1 100644 --- a/orkaudio/audiocaptureplugins/voip/Rtp.h +++ b/orkaudio/audiocaptureplugins/voip/Rtp.h @@ -44,6 +44,19 @@ public: }; typedef boost::shared_ptr<RtpPacketInfo> RtpPacketInfoRef; +class RtpEventInfo +{ +public: + void ToString(CStdString& string); + + unsigned short m_event; + unsigned short m_e; + unsigned short m_r; + unsigned short m_volume; + unsigned short m_duration; + unsigned int m_startTimestamp; +}; +typedef boost::shared_ptr<RtpEventInfo> RtpEventInfoRef; #endif diff --git a/orkaudio/audiocaptureplugins/voip/RtpSession.cpp b/orkaudio/audiocaptureplugins/voip/RtpSession.cpp index 9a3b46a..b133317 100644 --- a/orkaudio/audiocaptureplugins/voip/RtpSession.cpp +++ b/orkaudio/audiocaptureplugins/voip/RtpSession.cpp @@ -57,6 +57,11 @@ RtpSession::RtpSession(CStdString& trackingId) m_skinnyPassThruPartyId = 0; memset(m_localMac, 0, sizeof(m_localMac)); memset(m_remoteMac, 0, sizeof(m_remoteMac)); + m_currentRtpEvent = 65535; + m_lastEventEndSeqNo = 0; + m_currentDtmfDuration = 0; + m_currentRtpEventTs = 0; + m_currentDtmfVolume = 0; } void RtpSession::Stop() @@ -478,6 +483,85 @@ void RtpSession::ReportMetadata() g_captureEventCallBack(event, m_capturePort); } +void RtpSession::RecordRtpEvent() +{ + CaptureEventRef event(new CaptureEvent()); + CStdString dtmfEventString, dtmfEventKey; + + dtmfEventString.Format("event:%d timestamp:%d duration:%d volume:%d seqno:%d", m_currentRtpEvent, m_currentRtpEventTs, + m_currentDtmfDuration, m_currentDtmfVolume, m_currentSeqNo); + dtmfEventKey.Format("%d_RtpDtmfEvent", m_currentRtpEventTs); + event->m_type = CaptureEvent::EtKeyValue; + event->m_key = dtmfEventKey; + event->m_value = dtmfEventString; + g_captureEventCallBack(event, m_capturePort); + + //LOG4CXX_INFO(m_log, "[" + m_trackingId + "] Recording RTP event [ " + dtmfEventString + " ]"); +} + +void RtpSession::HandleRtpEvent(RtpPacketInfoRef& rtpPacket) +{ + CStdString logMsg; + + if(rtpPacket->m_payloadSize < sizeof(RtpEventPayloadFormat)) + { + LOG4CXX_WARN(m_log, "[" + m_trackingId + "] Payload size for event packet too small"); + return; + } + + RtpEventPayloadFormat *payloadFormat = (RtpEventPayloadFormat *)rtpPacket->m_payload; + RtpEventInfoRef rtpEventInfo(new RtpEventInfo()); + + rtpEventInfo->m_event = (unsigned short)payloadFormat->event; + rtpEventInfo->m_e = (payloadFormat->er_volume & 0x80) ? 1 : 0; + rtpEventInfo->m_r = (payloadFormat->er_volume & 0x40) ? 1 : 0; + rtpEventInfo->m_volume = (unsigned short)(payloadFormat->er_volume & 0x3F); + rtpEventInfo->m_duration = ntohs(payloadFormat->duration); + rtpEventInfo->m_startTimestamp = rtpPacket->m_timestamp; + + if((m_currentRtpEvent != 65535) && (m_currentRtpEvent != rtpEventInfo->m_event)) + { + RecordRtpEvent(); + } + else if(rtpEventInfo->m_e) + { + if((m_currentRtpEvent != 65535)) + { + m_currentDtmfDuration = rtpEventInfo->m_duration; + m_currentDtmfVolume = rtpEventInfo->m_volume; + m_currentRtpEventTs = rtpEventInfo->m_startTimestamp; + m_currentSeqNo = rtpPacket->m_seqNum; + + if(m_lastEventEndSeqNo != rtpPacket->m_seqNum) + { + RecordRtpEvent(); + m_lastEventEndSeqNo = rtpPacket->m_seqNum; + } + + m_currentRtpEvent = 65535; + } + + rtpEventInfo->m_event = 65535; + rtpEventInfo->m_duration = 0; + } + else if((m_currentRtpEvent != 65535) && m_currentDtmfDuration && (rtpEventInfo->m_duration < m_currentDtmfDuration)) + { + RecordRtpEvent(); + } + + if(!rtpEventInfo->m_e) + { + m_currentRtpEvent = rtpEventInfo->m_event; + } + + m_currentDtmfDuration = rtpEventInfo->m_duration; + m_currentDtmfVolume = rtpEventInfo->m_volume; + m_currentRtpEventTs = rtpEventInfo->m_startTimestamp; + m_currentSeqNo = rtpPacket->m_seqNum; + + return; +} + // Returns false if the packet does not belong to the session (RTP timestamp discontinuity) bool RtpSession::AddRtpPacket(RtpPacketInfoRef& rtpPacket) { @@ -510,6 +594,17 @@ bool RtpSession::AddRtpPacket(RtpPacketInfoRef& rtpPacket) } } + if(m_protocol == ProtSip) + { + /* Check if this is a telephone-event */ + if(rtpPacket->m_payloadType == StringToInt(m_telephoneEventPayloadType)) + { + // This is a telephone-event + HandleRtpEvent(rtpPacket); + return true; + } + } + // If we are on hold, unmark this if(m_onHold) { @@ -705,6 +800,7 @@ void RtpSession::ReportSipInvite(SipInviteInfoRef& invite) LOG4CXX_INFO(m_log, logMsg); } m_invites.push_front(invite); + m_telephoneEventPayloadType = invite->m_telephoneEventPayloadType; // Gather extracted fields std::copy(invite->m_extractedFields.begin(), invite->m_extractedFields.end(), std::inserter(m_tags, m_tags.begin())); diff --git a/orkaudio/audiocaptureplugins/voip/RtpSession.h b/orkaudio/audiocaptureplugins/voip/RtpSession.h index 02cc7ce..9c59593 100644 --- a/orkaudio/audiocaptureplugins/voip/RtpSession.h +++ b/orkaudio/audiocaptureplugins/voip/RtpSession.h @@ -42,6 +42,7 @@ public: bool m_validated; // true when an RTP stream has been seen for the INVITE bool m_attrSendonly; // true if the SDP has a:sendonly std::map<CStdString, CStdString> m_extractedFields; + CStdString m_telephoneEventPayloadType; time_t m_recvTime; }; @@ -145,6 +146,8 @@ private: void ProcessMetadataSkinny(RtpPacketInfoRef& rtpPacket); void ReportMetadata(); void GenerateOrkUid(); + void HandleRtpEvent(RtpPacketInfoRef& rtpPacket); + void RecordRtpEvent(); RtpPacketInfoRef m_lastRtpPacket; RtpPacketInfoRef m_lastRtpPacketSide1; @@ -174,6 +177,14 @@ private: TcpAddressList m_rtpAddressList; std::list<SipInviteInfoRef> m_invites; std::map<CStdString, CStdString> m_tags; + CStdString m_telephoneEventPayloadType; + + unsigned short m_currentRtpEvent; + unsigned int m_currentRtpEventTs; + unsigned int m_currentDtmfDuration; + unsigned int m_currentDtmfVolume; + unsigned int m_currentSeqNo; + unsigned int m_lastEventEndSeqNo; }; typedef boost::shared_ptr<RtpSession> RtpSessionRef; diff --git a/orkaudio/audiocaptureplugins/voip/VoIp.cpp b/orkaudio/audiocaptureplugins/voip/VoIp.cpp index d85d05c..2d3faba 100644 --- a/orkaudio/audiocaptureplugins/voip/VoIp.cpp +++ b/orkaudio/audiocaptureplugins/voip/VoIp.cpp @@ -1031,7 +1031,7 @@ bool TryRtp(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader, UdpH u_short dest = ntohs(udpHeader->dest); if(!(ntohs(udpHeader->source)%2) && !(ntohs(udpHeader->dest)%2) || DLLCONFIG.m_rtpDetectOnOddPorts) // udp ports usually even { - if((rtpHeader->pt <= 34 && rtpHeader->pt != 13) || rtpHeader->pt == 97 || rtpHeader->pt == 98) // pt=34 is H263 and is the last possible valid codec + if((rtpHeader->pt <= 34 && rtpHeader->pt != 13) || rtpHeader->pt == 97 || rtpHeader->pt == 98 || rtpHeader->pt > 98) // pt=34 is H263 and is the last possible valid codec, pt > 98 is for the case of SIP telephone-event { // pt=13 is CN (Comfort Noise) result = true; u_char* payload = (u_char *)rtpHeader + sizeof(RtpHeaderStruct); @@ -1470,6 +1470,7 @@ bool TrySipInvite(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader char* audioField = NULL; char* connectionAddressField = NULL; char* attribSendonly = memFindAfter("a=sendonly", (char*)udpPayload, sipEnd); + char* rtpmapAttribute = memFindAfter("\na=rtpmap:", (char*)udpPayload, sipEnd); if(fromField) { @@ -1583,6 +1584,34 @@ bool TrySipInvite(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader } } + if(rtpmapAttribute) + { + CStdString rtpPayloadType, nextToken; + char *nextStep = NULL; + + while(rtpmapAttribute && rtpmapAttribute < sipEnd) + { + GrabTokenSkipLeadingWhitespaces(rtpmapAttribute, sipEnd, rtpPayloadType); + nextToken.Format("%s ", rtpPayloadType); + nextStep = memFindAfter((char*)nextToken.c_str(), rtpmapAttribute, sipEnd); + + /* We need our "nextStep" to contain at least the length + * of the string "telephone-event", 15 characters */ + if(nextStep && ((sipEnd - nextStep) >= 15)) + { + if(strncasecmp(nextStep, "telephone-event", 15) == 0) + { + /* Our DTMF packets are indicated using + * the payload type rtpPayloadType */ + info->m_telephoneEventPayloadType = rtpPayloadType; + break; + } + } + + rtpmapAttribute = memFindAfter("\na=rtpmap:", rtpmapAttribute, sipEnd); + } + } + if((unsigned int)info->m_fromRtpIp.s_addr == 0) { // In case connection address could not be extracted, use SIP invite sender IP address |