Files
resiprocate/tfm/TestRtp.cxx
2022-06-01 12:22:35 +02:00

1141 lines
30 KiB
C++

#include "tfm/TestRtp.hxx"
#include "tfm/RtpEvent.hxx"
#include "rutil/Logger.hxx"
#include "rutil/Data.hxx"
#include "rutil/Socket.hxx"
#include "rutil/DnsUtil.hxx"
#include "resip/stack/SipMessage.hxx"
#include "Expect.hxx"
#include "CommonAction.hxx"
#include <ctime>
#include <functional>
#define RESIPROCATE_SUBSYSTEM resip::Subsystem::TEST
using namespace resip;
using namespace std;
//#define USE_XS_TIME
//#define HAS_PCAP
#ifdef HAS_PCAP
#include <pcap.h>
#endif
#define ETHERNET_HEADER_SIZE 14
#define IP_HEADER_SIZE 20
#define UDP_HEADER_SIZE 8
#define RTP_HEADER_SIZE 12
#define RTCP_HEADER_SIZE 8
#define OVERHEAD_SIZE (ETHERNET_HEADER_SIZE + IP_HEADER_SIZE + UDP_HEADER_SIZE)
#define SDP_USER "tfm"
#define SDP_SESSION_ID 1000
#define SDP_INITIAL_VERSION 1
#define SDP_SESSION_NAME "tfm call"
#define RTCP_SR 200
#define RTCP_RR 201
#define RTCP_SDES 202
#define RTCP_BYE 203
#define RTCP_APP 204
// RTP interval in ms
#define RTP_INTERVAL 20
// RTCP interval in seconds
#define RTCP_INTERVAL 1
// .bwc. Not used. Do we plan on ever uncommenting the code that uses this?
//static int
//timeval_subtract(timeval *result, timeval* x, timeval*y)
//{
// /* Perform the carry for the later subtraction by updating y. */
// if (x->tv_usec < y->tv_usec) {
// int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
// y->tv_usec -= 1000000 * nsec;
// y->tv_sec += nsec;
// }
// if (x->tv_usec - y->tv_usec > 1000000) {
// int nsec = (x->tv_usec - y->tv_usec) / 1000000;
// y->tv_usec += 1000000 * nsec;
// y->tv_sec -= nsec;
// }
//
// /* Compute the time remaining to wait.
// tv_usec is certainly positive. */
// result->tv_sec = x->tv_sec - y->tv_sec;
// result->tv_usec = x->tv_usec - y->tv_usec;
//
// /* Return 1 if result is negative. */
// return x->tv_sec < y->tv_sec;
//}
PacketSenderStatistics::PacketSenderStatistics() :
mSent(0)
{
}
void
PacketSenderStatistics::clear()
{
mSent = 0;
}
PacketReceiverStatistics::PacketReceiverStatistics() :
mReceived(0),
mLost(0)
{
}
void
PacketReceiverStatistics::clear()
{
mReceived = 0;
mLost = 0;
}
///////////////////////////////////////////////////////////////////////////////
TestRtp::TestRtp() :
mFdRtp(INVALID_SOCKET),
mFdRtcp(INVALID_SOCKET),
mFdVideoRtp(INVALID_SOCKET),
mFdVideoRtcp(INVALID_SOCKET),
mLocalAddrRtp("127.0.0.1", 8000, V4, UDP),
mLocalAddrRtcp("127.0.0.1", 8001, V4, UDP),
mLocalAddrVideoRtp("127.0.0.1", 8002, V4, UDP),
mLocalAddrVideoRtcp("127.0.0.1", 8003, V4, UDP),
mRemoteAddrRtp("0.0.0.0", 0, V4, UDP),
mRemoteAddrRtcp("0.0.0.0", 0, V4, UDP),
mRemoteSsrc(0),
mRemoteCodec(0),
mSenderStatistics(),
mReceiverStatistics(),
mHold2543(false),
mDescribeWellKnownCodec(true),
mPtime(0),
mPacketsRtp(),
mPacketsRtcp(),
mSendPackets(false)
{
#ifndef WIN32
mLocalSsrc = random();
// mLocalSsrc = 0;
#else
srand((unsigned int) time(NULL));
mLocalSsrc = rand() % 64000;
#endif
clean();
}
// ----------------------------------------------------------------------------
void
TestRtp::clean()
{
mLocalSdp = std::make_shared<SdpContents>();
mLocalSdp->session().name() = SDP_SESSION_NAME;
mLocalSdp->session().version() = 0;
mLocalSdp->session().origin()
= SdpContents::Session::Origin(SDP_USER, SDP_SESSION_ID, SDP_INITIAL_VERSION, SdpContents::IP4, Data::Empty);
mPtime = 0;
mHold2543 = false;
mDescribeWellKnownCodec = true;
mSendPackets = false;
addBasicAudioCodecs();
}
// ----------------------------------------------------------------------------
void
TestRtp::addBasicAudioCodecs()
{
const Codec & codec1 = SdpContents::Session::Codec::ULaw_8000;
const Codec & codec2 = SdpContents::Session::Codec::TelephoneEvent;
addAudioCodec(codec1.getName(), codec1.payloadType(), codec1.getRate(), codec1.parameters());
addAudioCodec(codec2.getName(), codec2.payloadType(), codec2.getRate(), codec2.parameters());
}
resip::Socket
TestRtp::openSocket(TransportType type)
{
Socket fd;
switch( type )
{
case UDP:
fd = ::socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
break;
case TCP:
case TLS:
default:
InfoLog(<< "Unsupported transport type");
return INVALID_SOCKET;
}
if( INVALID_SOCKET == fd )
{
int e = getErrno();
InfoLog(<< "Failed to create socket: " << strerror(e));
}
return fd;
}
void
TestRtp::open()
{
mFdRtp = openSocket(mLocalAddrRtp.getType());
mFdRtcp = openSocket(mLocalAddrRtcp.getType());
DebugLog(<< "Create RTP/RTCP socket fd: " << (unsigned int) mFdRtp << "/" << (unsigned int) mFdRtcp);
mFdVideoRtp = openSocket(mLocalAddrVideoRtp.getType());
mFdVideoRtcp = openSocket(mLocalAddrVideoRtcp.getType());
}
void
TestRtp::close()
{
closeSocket(mFdRtp);
closeSocket(mFdRtcp);
closeSocket(mFdVideoRtp);
closeSocket(mFdVideoRtcp);
}
Data
TestRtp::getName() const
{
Data buffer;
{
DataStream strm(buffer);
strm << "RTP end point " << mLocalAddrRtp;
}
return buffer;
}
bool
TestRtp::isRtpPacket(const Data& packet, uint32_t ssrc) const
{
if( packet.size() < RTP_HEADER_SIZE )
return false;
RtpHeader& header = *((RtpHeader*)(packet.data()));
if( ntohl(header.mSsrc) != ssrc )
return false;
return true;
}
bool
TestRtp::isRtcpPacket(const Data& packet, uint32_t ssrc) const
{
if( packet.size() < RTCP_HEADER_SIZE )
return false;
RtcpHeader& header = *((RtcpHeader*)(packet.data()));
if( ntohl(header.mSsrc) != ssrc )
return false;
switch( header.mPayloadType )
{
case RTCP_SR:
case RTCP_RR:
case RTCP_SDES:
case RTCP_BYE:
case RTCP_APP:
return true;
}
return true;
}
uint16_t
TestRtp::getSeqNo(const Packet& packet)
{
RtpHeader& header = *((RtpHeader*)(packet.mData.data()));
return ntohs(header.mSequenceNumber);
}
void
TestRtp::RtpPacketInfo()
{
uint16_t first = getSeqNo(mPacketsRtp.front());
uint16_t last = getSeqNo(mPacketsRtp.back());
int received = (int) mPacketsRtp.size();
InfoLog(<< "Received packates: " << received);
InfoLog( << "First: " << first << ", Last: " << last);
if( received <= 0 )
return;
int expected = (last - first + 1 + 65536) % 65536;
InfoLog(<< "Expected packets: " << expected);
InfoLog(<< "Loss rate: " << 100.0f * (expected - received)/(float)expected << "%");
/*
struct timeval firstTime = mPacketsRtp.front().mTimeStamp;
struct timeval diff;
list<Packet>::iterator it;
int i = 0;
for( it = mPacketsRtp.begin(); it != mPacketsRtp.end(); it++ )
{
timeval_subtract(&diff, &it->mTimeStamp, &firstTime);
InfoLog(<< "Packet: " << i++ << ", Time: " << diff.tv_sec << "." << diff.tv_usec);
}
*/
}
void
TestRtp::loadStream(const Data& file, uint32_t ssrc)
{
#ifdef HAS_PCAP
char errbuff[PCAP_ERRBUF_SIZE];
pcap_t* p = pcap_open_offline(file.c_str(), errbuff);
if( !p )
{
InfoLog(<< "Error reading PCAP file " << file << ": " << errbuff);
return;
}
while( 1 )
{
struct pcap_pkthdr h;
const u_char* pkt = pcap_next(p, &h);
if( NULL == pkt )
break;
int len = h.len;
if( len < OVERHEAD_SIZE )
continue;
Packet packet(h.ts, Data(pkt + OVERHEAD_SIZE, len - OVERHEAD_SIZE));
if( isRtpPacket(packet.mData, ssrc) )
mPacketsRtp.push_back(packet);
else if( isRtcpPacket(packet.mData, ssrc) )
mPacketsRtcp.push_back(packet);
};
pcap_close(p);
InfoLog( << "RTP/RTCP packets read: " << mPacketsRtp.size() << "/" << mPacketsRtcp.size());
RtpPacketInfo();
#else
InfoLog(<< "Reading pcap files disabled");
resip_assert(0);
#endif
}
int
TestRtp::bindSocket(resip::Socket fd, Tuple& addr)
{
if( ::bind( fd, &addr.getMutableSockaddr(), addr.length()) == SOCKET_ERROR )
{
int e = getErrno();
if ( e == EADDRINUSE )
ErrLog (<< "Address already in use " << addr);
else
ErrLog (<< "Could not bind socket to " << addr);
return -1;
}
bool ok = makeSocketNonBlocking(fd);
if( !ok )
{
ErrLog (<< "Could not make socket non-blocking ");
return -1;
}
return 0;
}
void
TestRtp::bind()
{
bindSocket(mFdRtp, mLocalAddrRtp);
bindSocket(mFdRtcp, mLocalAddrRtcp);
DebugLog(<< "Binding RTP socket to " << mLocalAddrRtp);
DebugLog(<< "Binding RTCP socket to " << mLocalAddrRtcp);
bindSocket(mFdVideoRtp, mLocalAddrVideoRtp);
bindSocket(mFdVideoRtcp, mLocalAddrVideoRtcp);
DebugLog(<< "Binding RTP socket to " << mLocalAddrVideoRtp);
DebugLog(<< "Binding RTCP socket to " << mLocalAddrVideoRtcp);
}
Data
TestRtp::recvPacket(resip::Socket fd, Tuple& addr)
{
char* buffer = new char[MaxBufferSize];
socklen_t slen = addr.length();
int len = recvfrom(fd, buffer, MaxBufferSize, 0, &addr.getMutableSockaddr(), &slen);
if ( len == SOCKET_ERROR )
{
int e = getErrno();
InfoLog(<< "Socket read error: " << strerror(e));
}
if (len == 0 || len == SOCKET_ERROR)
{
delete[] buffer;
buffer=0;
return Data::Empty;
}
return Data(Data::Take, buffer, len);
}
bool
TestRtp::parsePacket(const Data& data)
{
if( data.size() < RTP_HEADER_SIZE )
{
InfoLog(<< "Invalid RTP message received of size: " << (unsigned int) data.size());
return false;
}
// !jv! for now, assume that RTP header is 12 bytes
const RtpHeader& header = *((const RtpHeader*)data.data());
uint8_t remoteCodec = header.mPayloadType & 0x7f;
bool matchAudio = false;
bool matchVideo = false;
// simple matching of payload type. Given the incoming RTP packet payload type, search our local codecs in SDP
// to see if there's a match and whether it's for audio or video.
//
for (list<Medium>::iterator it = mLocalSdp->session().media().begin(); it != mLocalSdp->session().media().end(); it++)
{
for( list<Codec>::const_iterator j = it->codecs().begin(); j != it->codecs().end(); j++ )
{
if (remoteCodec == j->payloadType())
{
if (it->name() == SdpHelper::AudioMediaType)
matchAudio = true;
else if (it->name() == SdpHelper::VideoMediaType)
matchVideo = true;
}
}
}
if( !(matchAudio || matchVideo) )
{
InfoLog(<< "Invalid payload type received: " << remoteCodec << " of size " << (unsigned int) data.size());
return false;
}
uint32_t remoteSsrc = ntohl(header.mSsrc);
if( 0 == mRemoteSsrc )
{
InfoLog(<< "TestRtp::parsePacket(): RTP strem started");
// handleEvent(new RtpEvent(this, Rtp_StreamStarted));
mRemoteSsrc = remoteSsrc;
mRemoteCodec = remoteCodec;
}
if( remoteSsrc != mRemoteSsrc )
{
InfoLog(<< "TestRtp::parsePacket(): SSRC changed to " << remoteSsrc);
// handleEvent(new RtpEvent(this, Rtp_SsrcChanged));
mRemoteSsrc = remoteSsrc;
}
if( mRemoteCodec != remoteCodec )
{
// handleEvent(new RtpEvent(this, Rtp_CodecChanged));
mRemoteCodec = remoteCodec;
}
mReceiverStatistics.mReceived++;
return true;
}
void
TestRtp::overrideSsrc(Data& packet)
{
RtpHeader& header = *((RtpHeader*)packet.data());
header.mSsrc = htonl(mLocalSsrc);
}
void
TestRtp::sendPacket(resip::Socket fd, Tuple& dest, const Data& data)
{
size_t count = sendto(fd, data.data(), (int) data.size(), 0, &dest.getSockaddr(), dest.length());
if( count == (size_t)SOCKET_ERROR )
{
int e = getErrno();
ErrLog(<< "Failed to send packet to " << dest << ": " << strerror(e));
}
else
{
if( count != data.size() )
ErrLog(<< "Buffer overflow while sending packet to " << dest);
}
mSenderStatistics.mSent++;
}
// ----------------------------------------------------------------------------
std::shared_ptr<SdpContents>
TestRtp::getLocalSdp() const
{
return getLocalSdp(MEDIA_ACTIVE, MEDIA_ACTIVE);
}
// ----------------------------------------------------------------------------
std::shared_ptr<SdpContents>
TestRtp::getLocalSdp(unsigned long audioAttr, unsigned long videoAttr) const
{
SdpContents * contents = dynamic_cast<SdpContents*>(mLocalSdp->clone());
mLocalSdp->session().origin().getVersion() += 1;
if (! mSessionName.empty())
mLocalSdp->session().name() = mSessionName;
std::shared_ptr<SdpContents> sdp(contents);
SdpContents::AddrType addrType = mLocalAddrRtp.ipVersion() == V4 ? SdpContents::IP4 : SdpContents::IP6;
Data addr = Tuple::inet_ntop(mLocalAddrRtp);
sdp->session().origin().setAddress(addr, addrType);
SdpContents::Session::Connection connection(addrType, addr);
if (mHold2543 && (audioAttr & MEDIA_HOLD))
connection.setAddress("0.0.0.0");
sdp->session().connection() = connection;
int audioPort = mLocalAddrRtp.getPort();
int offset = 0;
// for each m-line in the session, set here:
// * port
// * media-direction
//
for (list<Medium>::iterator it = sdp->session().media().begin(); it != sdp->session().media().end(); it++)
{
Medium & mline = *it;
unsigned long mediaAttr = MEDIA_NONE;
if (mline.name() == SdpHelper::AudioMediaType)
mediaAttr = audioAttr;
else if (mline.name() == SdpHelper::VideoMediaType)
mediaAttr = videoAttr;
else
continue;
// port
if ((mediaAttr & MEDIA_DISABLE))
{
mline.port() = 0;
}
else
{
mline.port() = audioPort + offset;
offset += 2;
}
// media-direction
if (! mHold2543)
{
if ((mediaAttr & MEDIA_HOLD))
mline.addAttribute("sendonly");
else if ((mediaAttr & MEDIA_ACTIVE))
mline.addAttribute("sendrecv");
else if ((mediaAttr & MEDIA_INACTIVE))
mline.addAttribute("inactive");
else if ((mediaAttr & MEDIA_HOLD_PEER))
mline.addAttribute("recvonly");
}
if (mPtime && (mline.name() == SdpHelper::AudioMediaType))
mline.addAttribute("ptime:" + Data(mPtime));
}
return sdp;
}
void
TestRtp::setLocalAddr(const Uri& addr)
{
int port = addr.port();
Tuple t(addr.host(), port, (DnsUtil::isIpV6Address(addr.host()) ? V6 : V4), UDP);
mLocalAddrRtp = t;
t.setPort(port + 1);
mLocalAddrRtcp = t;
t.setPort(port + 2);
mLocalAddrVideoRtp = t;
t.setPort(port + 3);
mLocalAddrVideoRtcp = t;
}
void
TestRtp::setLocalAddr(const Data& addr)
{
int port = mLocalAddrRtp.getPort();
Tuple t(addr, port, (DnsUtil::isIpV6Address(addr) ? V6 : V4), UDP);
mLocalAddrRtp = t;
t.setPort(port + 1);
mLocalAddrRtcp = t;
t.setPort(port + 2);
mLocalAddrVideoRtp = t;
t.setPort(port + 3);
mLocalAddrVideoRtcp = t;
}
void
TestRtp::addCodec(
const resip::Data& mediaName,
const resip::Data& codecName,
int payload,
int rate,
const resip::Data& parameters,
int port,
const char * proto)
{
if (mediaName.empty())
return;
if (! mLocalSdp)
mLocalSdp = std::make_shared<SdpContents>();
Medium * medium = 0;
// find the m-line based on media-type: 'audio'/'video', etc. in the REVERSE direction.
for (list<Medium>::reverse_iterator it = mLocalSdp->session().media().rbegin(); it != mLocalSdp->session().media().rend(); it++)
{
if (it->name() == mediaName)
medium = &(*it);
}
if (! medium)
{
// doesn't exist, add medium and codec
Medium tmpMedium(mediaName, port, 0, (proto ? proto : SdpHelper::RtpAvpProtocol));
mLocalSdp->session().addMedium(tmpMedium);
medium = &mLocalSdp->session().media().back();
}
if (mDescribeWellKnownCodec)
{
Codec codec(codecName, payload, rate);
codec.parameters() = parameters;
medium->codecs().push_back(codec);
}
else
{
Data strPayload = Data::from(payload);
medium->addFormat(strPayload);
// describe telephone-event anyhow
if (codecName == "telephone-event")
{
Data strRTPMap;
{
DataStream s(strRTPMap);
s <<strPayload <<' ' <<codecName <<'/' <<rate;
}
medium->addAttribute("rtpmap", strRTPMap);
if (! parameters.empty())
medium->addAttribute("fmtp", (strPayload + " " + parameters));
}
}
}
void
TestRtp::addControlMLine()
{
Medium medium(SdpHelper::ControlMediaType, 0, 0, SdpHelper::TcpMcProtocol);
mLocalSdp->session().addMedium(medium);
}
void
TestRtp::removeCodecs(const resip::Data& mediaName)
{
if (mLocalSdp)
{
if (mediaName.empty())
{
mLocalSdp->session().media().clear();
}
else
{
for (list<Medium>::iterator it = mLocalSdp->session().media().begin(); it != mLocalSdp->session().media().end();)
{
list<Medium>::iterator prev = it++;
if (prev->name() == mediaName)
mLocalSdp->session().media().erase(prev);
}
}
}
}
void
TestRtp::setRemoteAddr(const Tuple& addr)
{
mRemoteAddrRtp = addr;
mRemoteAddrRtcp = addr;
int port = mRemoteAddrRtp.getPort();
mRemoteAddrRtcp.setPort(port + 1);
}
void
TestRtp::enableSendingDelegate(bool enable)
{
DebugLog(<< "enableSendingDelegate()");
mSendPackets = enable;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
resip::Data SdpHelper::AudioMediaType = "audio";
resip::Data SdpHelper::VideoMediaType = "video";
resip::Data SdpHelper::ControlMediaType = "control";
resip::Data SdpHelper::RtpAvpProtocol = "RTP/AVP";
resip::Data SdpHelper::TcpMcProtocol = "tcp mc";
SdpHelper::MediaDirection
SdpHelper::getMediaDirectionFromString(const char * szMediaName)
{
if (szMediaName)
{
Data dir(Data::Share, szMediaName);
if (dir == "sendonly") return MD_SendOnly;
else if (dir == "recvonly") return MD_RecvOnly;
else if (dir == "sendrecv") return MD_SendRecv;
else if (dir == "inactive") return MD_Inactive;
}
return MD_None;
}
SdpHelper::MediaDirection
SdpHelper::getMediaDirection(std::shared_ptr<resip::SipMessage> msg, const char * szMediaName)
{
SdpContents* sdp = dynamic_cast<SdpContents*>(msg->getContents());
if (!sdp || !szMediaName)
return MD_None;
for (list<Medium>::const_iterator it = sdp->session().media().begin(); it != sdp->session().media().end(); it++)
{
if (it->name() == szMediaName)
{
if (it->exists(Data(Data::Share, "inactive"))) return MD_Inactive;
else if (it->exists(Data(Data::Share, "recvonly"))) return MD_RecvOnly;
else if (it->exists(Data(Data::Share, "sendonly"))) return MD_SendOnly;
else if (it->exists(Data(Data::Share, "sendrecv"))) return MD_SendRecv;
}
}
return MD_None;
}
bool
SdpHelper::hasIce(std::shared_ptr<SipMessage> msg)
{
SdpContents* sdp = dynamic_cast<SdpContents*>(msg->getContents());
if( NULL == sdp )
return false;
list<SdpContents::Session::Medium> media = sdp->session().media();
// only consider first media
list<SdpContents::Session::Medium>::const_iterator it = media.begin();
if( it == media.end() )
return false;
if( it->exists("alt") )
return true;
if( it->exists("candidate") )
return true;
return false;
}
Data
SdpHelper::getConnectionAddr(std::shared_ptr<resip::SipMessage> msg)
{
SdpContents* sdp = dynamic_cast<SdpContents*>(msg->getContents());
if( NULL == sdp )
{
return "0.0.0.0";
}
return sdp->session().connection().getAddress();
}
int
SdpHelper::getPort(std::shared_ptr<resip::SipMessage> msg, unsigned int index)
{
SdpContents* sdp = dynamic_cast<SdpContents*>(msg->getContents());
if( NULL == sdp )
{
return -1;
}
list<SdpContents::Session::Medium> media = sdp->session().media();
if( media.size() < index + 1 )
return -1;
list<SdpContents::Session::Medium>::iterator it = media.begin();
for( unsigned int i = 0; i < index; it++, i++ );
return it->port();
}
int
SdpHelper::getPort(std::shared_ptr<resip::SipMessage> msg, const char * szMediaName)
{
SdpContents* sdp = dynamic_cast<SdpContents*>(msg->getContents());
if (!sdp || !szMediaName)
return -1;
for (list<Medium>::const_iterator it = sdp->session().media().begin(); it != sdp->session().media().end(); it++)
{
if (it->name() == szMediaName)
return it->port();
}
return -1;
}
void
SdpHelper::setPort(std::shared_ptr<resip::SdpContents> sdp, const char * szMediaName, unsigned long port)
{
if (!sdp || !szMediaName)
return;
for (list<Medium>::iterator it = sdp->session().media().begin(); it != sdp->session().media().end(); it++)
{
if (it->name() == szMediaName)
{
it->port() = port;
break;
}
}
}
void SdpHelper::addAttr(
std::shared_ptr<resip::SdpContents> sdp,
const char * szMediaName,
const char* szAttrField,
const char* szAttrValue)
{
if (!sdp || !szMediaName || !szAttrField || !szAttrValue)
return;
for (list<Medium>::iterator it = sdp->session().media().begin(); it != sdp->session().media().end(); it++)
{
if (it->name() == szMediaName)
{
it->addAttribute(szAttrField, szAttrValue);
break;
}
}
}
bool
SdpHelper::isMediaInactive(std::shared_ptr<SipMessage> msg)
{
SdpContents* sdp = dynamic_cast<SdpContents*>(msg->getContents());
if( NULL == sdp )
return false;
list<SdpContents::Session::Medium> media = sdp->session().media();
// only consider first media
list<SdpContents::Session::Medium>::const_iterator it = media.begin();
if( it == media.end() )
return false;
if( it->exists("inactive") )
return true;
return false;
}
bool
SdpHelper::isMediaSendOnly(std::shared_ptr<SipMessage> msg)
{
SdpContents* sdp = dynamic_cast<SdpContents*>(msg->getContents());
if( NULL == sdp )
return false;
list<SdpContents::Session::Medium> media = sdp->session().media();
// only consider first media
list<SdpContents::Session::Medium>::const_iterator it = media.begin();
if( it == media.end() )
return false;
if( it->exists("sendonly") )
return true;
return false;
}
bool
SdpHelper::isMediaRecvOnly(std::shared_ptr<SipMessage> msg)
{
SdpContents* sdp = dynamic_cast<SdpContents*>(msg->getContents());
if( NULL == sdp )
return false;
list<SdpContents::Session::Medium> media = sdp->session().media();
// only consider first media
list<SdpContents::Session::Medium>::const_iterator it = media.begin();
if( it == media.end() )
return false;
if( it->exists("recvonly") )
return true;
return false;
}
unsigned int
SdpHelper::getMediaCount(std::shared_ptr<resip::SipMessage> msg)
{
SdpContents* sdp = dynamic_cast<SdpContents*>(msg->getContents());
if (! sdp)
return 0;
return (unsigned int) sdp->session().media().size();
}
// m-line
unsigned int
SdpHelper::getMLineCount(std::shared_ptr<resip::SipMessage> msg, const char * szMediaName)
{
unsigned int ret = 0;
SdpContents* sdp = dynamic_cast<SdpContents*>(msg->getContents());
if (! sdp)
return 0;
for (list<Medium>::const_iterator it = sdp->session().media().begin(); it != sdp->session().media().end(); it++)
{
if (it->name() == szMediaName)
ret++;
}
return ret;
}
unsigned int
SdpHelper::getCodecsCount(std::shared_ptr<resip::SipMessage> msg, const char * szMediaName)
{
SdpContents* sdp = dynamic_cast<SdpContents*>(msg->getContents());
if (! sdp)
return 0;
for (list<Medium>::const_iterator it = sdp->session().media().begin(); it != sdp->session().media().end(); it++)
{
if (it->name() == szMediaName)
return (unsigned int) it->codecs().size();
}
return 0;
}
bool
SdpHelper::hasPayloadNumber(std::shared_ptr<resip::SipMessage> msg, int payloadNumber)
{
SdpContents* sdp = dynamic_cast<SdpContents*>(msg->getContents());
if (! sdp)
return false;
for (list<Medium>::const_iterator it = sdp->session().media().begin(); it != sdp->session().media().end(); it++)
{
for( list<Codec>::const_iterator jIt = it->codecs().begin(); jIt != it->codecs().end(); jIt++ )
{
if (jIt->payloadType() == payloadNumber)
return true;
}
}
return false;
}
bool
SdpHelper::isHold2543(std::shared_ptr<SipMessage> msg)
{
SdpContents* sdp = dynamic_cast<SdpContents*>(msg->getContents());
if( NULL == sdp )
return false;
SdpContents::Session::Connection connection = sdp->session().connection();
Data addr = connection.getAddress();
return addr == "0.0.0.0";
}
void
TestRtp::thread()
{
// Three types of modes:
// 1. Return each packet to sender with modified SSRC
// 2. When a packets comes in from sender, send a packet from file
// 3. Send packet from file in regular interval. Currently, this method
// sends packets out according to timestamp found in Ethereal.
// The current solution consumes all CPU since it spins and checks
// the time periodically
// time_t lastRtcp = time(NULL);
struct timeval lastPacketTime;
if( !mPacketsRtp.empty() )
lastPacketTime = mPacketsRtp.front().mTimeStamp;
#ifdef USE_XS_TIME
double lastSendingTime = XsLib::XsGetClockTickUs();
#elif !defined(WIN32)
struct timeval lastSendingTime;
gettimeofday(&lastSendingTime, 0);
#endif
while( !isShutdown() )
{
FdSet fdSet;
fdSet.setRead(mFdRtp);
fdSet.setRead(mFdRtcp);
/*int ready = */fdSet.selectMilliSeconds(RTP_INTERVAL);
if( fdSet.readyToRead(mFdRtp) )
{
Tuple from;
Data data = recvPacket(mFdRtp, from);
if( 0 == mRemoteAddrRtp.getPort() )
{
InfoLog(<< "Updating RTP address changed to " << from);
mRemoteAddrRtp = from;
}
if( parsePacket(data) )
{
// overrideSsrc(data);
// sendPacket(mFdRtp, mRemoteAddrRtp, data);
if( mSendPackets && !mPacketsRtp.empty() )
{
Packet packet = mPacketsRtp.front();
mPacketsRtp.pop_front();
sendPacket(mFdRtp, mRemoteAddrRtp, packet.mData);
if( 0 == (mPacketsRtp.size() % 100) )
InfoLog(<< (unsigned int) mPacketsRtp.size() << " more packets to send");
}
}
}
/*
// rtcp
if( fdSet.readyToRead(mFdRtcp) )
{
Tuple from;
Data data = recvPacket(mFdRtcp, from);
if( !(mRemoteAddrRtcp == from) )
{
InfoLog(<< "Remote RTCP address changed to " << from);
mRemoteAddrRtcp = from;
}
}
*/
#if 0
if( mSendPackets )
{
if( !mPacketsRtp.empty() )
{
// DebugLog(<< "Checking time to send RTP packet");
/*
#ifdef USE_XS_TIME
double currentTime = XsLib::XsGetClockTickUs();
double elapsedTime = currentTime - lastSendingTime;
#else
struct timeval currentTime;
gettimeofday(&currentTime, 0);
struct timeval elapsedTime;
timeval_subtract(&elapsedTime, &currentTime, &lastSendingTime);
#endif
struct timeval currentPacketTime = mPacketsRtp.front().mTimeStamp;
struct timeval packetDiffTime;
timeval_subtract(&packetDiffTime, &currentPacketTime, &lastPacketTime);
// InfoLog( << "Packet diff: " << packetDiffTime.tv_sec << "." << packetDiffTime.tv_usec);
#ifdef USE_XS_TIME
// InfoLog(<< elapsedTime);
bool send = (packetDiffTime.tv_sec * 1000000 + packetDiffTime.tv_usec) >= elapsedTime;
#else
struct timeval diff;
bool send = (timeval_subtract(&diff, &elapsedTime, &packetDiffTime) == 0);
// InfoLog( << "Elapsed: " << elapsedTime.tv_sec << "." << elapsedTime.tv_usec);
#endif
*/
bool send = true;
if( send )
{
// DebugLog(<< "Sending RTP packet");
Packet packet = mPacketsRtp.front();
mPacketsRtp.pop_front();
sendPacket(mFdRtp, mRemoteAddrRtp, packet.mData);
/*
lastPacketTime = currentPacketTime;
lastSendingTime = currentTime;
*/
}
}
/*
if( 0 != ready )
#ifdef WIN32
Sleep(RTP_INTERVAL);
#else
usleep(RTP_INTERVAL * 1000);
#endif
*/
/*
if( !mPacketsRtcp.empty() )
{
if( time(NULL) > lastRtcp + RTCP_INTERVAL )
{
DebugLog(<< "Sending RTCP packet");
Packet packet = mPacketsRtcp.front();
mPacketsRtcp.pop_front();
sendPacket(mFdRtcp, mRemoteAddrRtcp, packet.mData);
lastRtcp = time(NULL);
}
}
*/
}
#endif
}
}
TestEndPoint::ExpectBase*
TestRtp::expect(RtpEvent::Type type,
int timeoutMs,
ActionBase* expectAction)
{
InfoLog( << "TestRtp::expect()");
return new Expect(*this,
new EventMatcherSpecific<RtpEvent>(type),
timeoutMs,
expectAction);
}
ActionBase*
TestRtp::enableSending(bool enable)
{
DebugLog(<< "TestRtp::enableSending()");
return new CommonAction(this,
"Enable sending",
std::bind(&TestRtp::enableSendingDelegate, this, enable));
}