Making a Call

Overview

Making a call starts with sending a SIP INVITE message with an SDP describing on which IP address(es) and port(s) the application expects to receive audio or video packets. The format of these candidates is specified for example by the MMUSIC working group of the IETF [3]. When using the AnyFirewall™ Engine, these candidate fields are populated with parameters obtained from the AnyFirewall™ Engine. These strings can be used to populate the candidate fields in the SDP part of a SIP INVITE message.

In this section, we show how to create audio channels for sending and receiving voice packets, obtaining the candidates for the SDP and sending the SIP INVITE message. Finally, the call completion with the callee is described.

Creating an audio channel

…
m_iAudioChannelRTP  = m_pAFEngine->Create(serverStoreID, AF_CHANNEL_RTP, 0, 0, 0);
m_iAudioChannelRTCP = m_pAFEngine->Create(serverStoreID, AF_CHANNEL_RTCP, 0, 0, 0);
…
If client wants ICE over TCP along with UDP then the call should be,
…
m_iAudioChannelRTP = m_pAFEngine->Create(serverStoreID, AF_CHANNEL_RTP, 0, 0, -1);
m_iAudioChannelRTCP= m_pAFEngine->Create(serverStoreID, AF_CHANNEL_RTCP, 0, 0, -1);
…
The audio channels are created using the parameter AF_CHANNEL_RTP and AF_CHANNEL_RTCP, which are a specialized version of AF_CHANNEL_UDP. This describes a channel for sending and receiving RTP/RTCP packets.
Other potential types used for different purposes are AF_CHANNEL_UDP, AF_CHANNEL_LOOPBACK, AF_CHANNEL_TCP, AF_CHANNEL_DATA, AF_CHANNEL_TLS and AF_CHANNEL_BFCP. TCP and TLS channels are only used for client/server connections, e.g. in order to establish a TCP connection to a SIP proxy.


Obtaining candidate parameters of the audio channels

…
char sMediaStreamSpec[100];   
sprintf(sMediaStreamSpec, "%s %d %d",
                    AF_MEDIA_STREAM_AUDIO,
                    m_iAudioChannelRTP,
                    m_iAudioChannelRTCP
       );
m_iSession = m_pAFEngine->CreateSession(serverStoreID, sMediaStreamSpec, NULL);
const struct AfSessionInfo *sessionInfo = m_pAFEngine->MakeOffer(m_iSession);
…

Here, the method CreateSession creates a session with one media stream AF_MEDIA_STREAM_AUDIO and two audio channels in that media stream. The function returns a unique session ID that is used to call the MakeOffer API that initializes all the channels (e.g., detects public IP address and port) belonging to that session, and returns a SDP structure describing the possible IP addresses, ports and protocols that can be used to connect to the client application.

This information must be transmitted to the callee.


How to deal with IPv6 candidate in SDP

AfMedia structure and AfSessionInfo structure holds the address family information of local and remote peer to let client create SDP properly.

There is a new member named as isIPv6Candidate under AfMedia structure, which holds the address family information of pcCandidate[index] local candidate.
There is a new member named as isRemotePeerSupportsIPv6 under AfSessionInfo structure, which holds the address family information of remote peer, whether remote peer supports IPv6 address or not. Remote Information will be retrieved when client receives SDP from remote peer.
  • If isIPv6Candidate[index] value is 0, pcCandidate[index] candidate is IPv4 address.
  • If isIPv6Candidate[index] value is 1, pcCandidate[index] candidate is IPv6 address.
  • If isRemotePeerSupportsIPv6 value is 0, peer supports only IPv4 address family.
  • If isRemotePeerSupportsIPv6 value is 1, peer supports dual-stack or IPv6 address family.


Sending a SIP Invite

In order to initiate a call, the same channel as created for the registration is used to send the SIP INVITE message.

The SDP structure returned by MakeOffer can be used to construct the SDP string of a SIP message.

// CreateSIPINVITE must be implemented somewhere
std::string sAudioSDPInfo = CreateSdpFromStruct(sessionInfo);
string sInviteMsg = m_pSipUtil->BuildInviteRequestMsg(
                            m_sUsername,
                            sRemoteUser,
                            m_sMyRaddr,
                            m_iMyRport,
                            sAudioSDPInfo,
                            m_sSipProxyDomain,
                            sCalleeAddress,
                            sCallID,
                            false,
                            m_sBranchTag,
                            m_sFromTag,
                            "",
                            m_iTransportType
                  );
if (m_pAFEngine->Send(m_iSIPChannel, sInviteMsg.c_str(),
                            (int)sInviteMsg.length(),
                            AF_BLOCKING) < 0
                  )
{
    // Handle errors here
}
…

In this particular example, the method Send blocks (AF_BLOCKING) until the message could be inserted into the internal buffers used by the AnyFirewall™ Engine. Instead of defining AF_BLOCKING, it is also possible to set a timeout (in milliseconds) or use AF_NON_BLOCKING.


Responding to a SIP INVITE Message

The callee essentially follows the same steps as the caller. Upon receiving a SIP INVITE, the callee creates the channels and the session that will be used for communicating with the caller. Then the callee uses MakeAnswer() API to process the SDP received from its remote peer. Moreover, the function returns an SDP description for the callee that can be used in 200 OK responses. The call to this function also starts ICE checks [3].

m_iAudioChannelRTP  = m_pAFEngine->Create(serverStoreID, AF_CHANNEL_RTP, 0, 0, 0);
m_iAudioChannelRTCP = m_pAFEngine->Create(serverStoreID, AF_CHANNEL_RTCP, 0, 0, 0);

For ICE over TCP along with UDP use:

m_iAudioChannelRTP  = m_pAFEngine->Create(serverStoreID, AF_CHANNEL_RTP, 0, 0, -1);
m_iAudioChannelRTCP = m_pAFEngine->Create(serverStoreID, AF_CHANNEL_RTCP, 0, 0, -1);
char sMediaStreamSpec[100];  
sprintf(sMediaStreamSpec, "%s %d %d",
                    AF_MEDIA_STREAM_AUDIO,
                    m_iAudioChannelRTP,
                    m_iAudioChannelRTCP
        );
m_iSession = m_pAFEngine-> CreateSession (serverStoreID, sMediaStreamSpec,NULL);
const struct AfSessionInfo *sessionInfo = m_pAFEngine->MakeAnswer(m_iSession, sRemoteSDP, sPeerID);
std::string sAudioSDPInfo = CreateSdpFromStruct(sessionInfo);


How to deal with insufficient bandwidth

In Microsoft® Lync™ mode, Callee side is the bandwidth management agent. For a particular offer from caller, bandwidth availability is checked in the first call to AFE's MakeAnswer() or MakeAnswerAsync() API function. This function returns a structure - AfSessionInfo.

Before constructing answerSDP, AFE client must check AfSessionInfo's data member  bandwidthAvailable:
  • If value is 0, bandwidth is not available for any of the media included in the session. AFE client must close the session and cancel the call.
  • If value is 1, bandwidth is available for at least one of the media included in the session. AFE client should proceed in this case. The AFE client iterates through each of the media in the media list ppMedia of AfSessionInfo structure. Each media is represented by AfMedia structure. Bandwidth availability for each media can be checked by bandwidthAvailable data member of AfMedia structure. AFE client must iterate through each AfMedia and check the value of bandwidthAvailable:
    • If value is 0, bandwidth is unavailable for this media. Client adds m line with port 0 to the SDP. The client then adds a diagnostic message indicating insufficient bandwidth. Client does not need to close the channels associated with this media because AFE handles this issue.
    • If value is 1, bandwidth is available for this media. AFE client adds necessary attributes to the SDP for this media.


Establishing an Audio Call with a Callee

Once the caller receives 200 OK, it uses ProcessAnswer API to process the remote SDP and start ICE checks [3].

…
m_pAFEngine->ProcessAnswer(m_iSession, sSDP, successAnswerReceived, sPeerID);
…