download Download: call-hold.zip

In this guide you will find all the information required to implement call holding function into your softphone using Ozeki VoIP SIP SDK. To use this example, you need to have Ozeki VoIP SIP SDK installed, and a reference to ozeki.dll should be added to your visual studio project. It's also recommended to visit the SIP INVITE and How to accept incoming call articles before you begin to study how to perform holding a call.

putting on hold an established call
Figure 1 - Putting on hold an established call

What does hold a call mean? When is it needed?

There are cases in remote communication when we need to wait for the callee for some reason. Holding a call means that the call was accepted but the caller has to wait for a call transfer or for some other action (Figure 1).

The most essential case of call holding is when you call a call center or a customer service where there is a limited number of agents at a time. In the case when all the agents are engaged, the caller needs to hold the call and wait for an agent to get free. In the most usual cases the call holding is indicated by the call center and the caller receives some music played during the waiting period.

How to put a call on Hold using C#?

Call holding does not differ so much from the original softphone concept. Ozeki VoIP SIP SDK provides the ToggleHold() method for a call that will do the trick for you. With this method you do not even need to check the state of the call, or store/check whether the call is on hold or not. This method does the work for you. If the call is on hold, the method will unhold it. In the case of unhold, the call will be put on hold.

The ToogleHold() method can set the call state to LocalHeld and RemoteHeld too. The LocalHeld state indicates that the call was held on the local computer, while the RemotHeld shows that the call was held by the remote party.

Call holding example in C#

using System;
using System.Threading;
using Ozeki.VoIP;

namespace Call_hold
{
    class Program
    {
        static ISoftPhone softphone;   // softphone object
        static IPhoneLine phoneLine;   // phoneline object
        static IPhoneCall call;

        private static void Main(string[] args)
        {
            // Create a softphone object with RTP port range 5000-10000
            softphone = SoftPhoneFactory.CreateSoftPhone(5000, 10000);

            // SIP account registration data, (supplied by your VoIP service provider)
            var registrationRequired = true;
            var userName = "858";
            var displayName = "858";
            var authenticationId = "858";
            var registerPassword = "858";
            var domainHost = "192.168.115.100";
            var domainPort = 5060;

            var account = new SIPAccount(registrationRequired, displayName, userName, authenticationId, registerPassword, domainHost, domainPort);

            // Send SIP regitration request
            RegisterAccount(account);

            // Prevents the termination of the application
            while (true) Thread.Sleep(50);
        }

        static void RegisterAccount(SIPAccount account)
        {
            try
            {
                phoneLine = softphone.CreatePhoneLine(account);
                phoneLine.RegistrationStateChanged += line_RegStateChanged;
                softphone.RegisterPhoneLine(phoneLine);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error during SIP registration: " + ex);
            }
        }

        static void line_RegStateChanged(object sender, RegistrationStateChangedArgs e)
        {
            if (e.State == RegState.NotRegistered || e.State == RegState.Error)
                Console.WriteLine("Registration failed!");

            if (e.State == RegState.RegistrationSucceeded)
            {
                Console.WriteLine("Registration succeeded - Online!");
                CreateCall();
            }
        }

        static void CreateCall()
        {
            var numberToDial = "853";
            call = softphone.CreateCallObject(phoneLine, numberToDial);
            call.CallStateChanged += call_CallStateChanged;
            call.Start();
        }
  
        static void call_CallStateChanged(object sender, CallStateChangedArgs e)  
        {  
            Console.WriteLine("Call state: {0}.", e.State);

             if (e.State == CallState.InCall && e.State != CallState.LocalHeld)
             {
                 Console.WriteLine("Press ENTER to put your call on hold.");
                 Console.ReadLine();
                 call.Hold();
             }

             if (e.State == CallState.LocalHeld)
             {
                 Console.WriteLine("Press ENTER to take your call off hold.");
                 Console.ReadLine();
                 call.Unhold();
             }
        }
    }  
}

Communication through the network

After the call has been established between two parties, the call can be put on hold; It can be done by sending SIP INVITE request, which sets the "Media Attribute" to "sendonly" and "inactive" at the communicating parties.
The following steps are representing the holding function, after the call has been established (so, the ACK message has been sent):

Step 1: The softphone puts the call on hold by sending an INVITE request to set the media attributes (UDP message, Softphone -> PBX)

INVITE sip:9999@192.168.112.215:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.112.215:6848;branch=z9hG4bKdad4b02d-105a-40f7-a6e2-
2f08327c787a;rport
To: <sip:9999@192.168.112.215>;tag=dxrgsygo
From: "1000"<sip:1000@192.168.112.215>;tag=llgsishn
CSeq: 4 INVITE
Call-ID: fvlwmncqufangggiqyakimoxupwqgjnujjbttehrraqckdvgrj
Max-Forwards: 70
Contact: <sip:1000@192.168.112.215:6848>
User-Agent: Ozeki VoIP SIP SDK v10.1.8
Content-Type: application/sdp
Proxy-Authorization:Digest username="1000",realm="OzekiPBX",
nonce="d5a4e0e18c60423fb5ce0d1b19c960dd",
response="50aa459d404d4e0ef0d337e50b950218",uri="sip:9999@192.168.112.215:5060",
algorithm=MD5
Content-Length: 589

v=0
o=- 1557342975 1557342976 IN IP4 192.168.112.215
s=Ozeki VoIP SIP SDK v10.1.8
c=IN IP4 192.168.112.215
t=0 0
m=audio 6677 RTP/AVP 8
a=rtpmap:8 PCMA/8000
a=sendonly
m=video 7937 RTP/AVP 34
a=rtpmap:34 H263/90000
a=fmtp:34 QCIF=1;CIF=1
a=inactive

Step 2: The other party sends back the SIP 200 OK message via the PBX (UDP message, PBX -> Softphone)

SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.112.215:6848;branch=z9hG4bKdad4b02d-105a-40f7-a6e2-
2f08327c787a;rport=6848;received=192.168.112.215
From: "1000"<sip:1000@192.168.112.215>;tag=llgsishn
Call-ID: fvlwmncqufangggiqyakimoxupwqgjnujjbttehrraqckdvgrj
CSeq: 4 INVITE
To: <sip:9999@192.168.112.215>;tag=dxrgsygo
Contact: <sip:9999@192.168.112.215:5060>
User-Agent: Ozeki Phone System XE v5.3.1
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REGISTER, SUBSCRIBE, NOTIFY, REFER, 
INFO, MESSAGE
Content-Type: application/sdp
Content-Length: 495

v=0
o=- 724034129 559580418 IN IP4 192.168.112.215
s=Ozeki Call
c=IN IP4 192.168.112.215
t=0 0
m=audio 5673 RTP/AVP 8
a=rtpmap:8 PCMA/8000
a=recvonly
m=video 8748 RTP/AVP 34
a=rtpmap:34 H263/90000
a=fmtp:34 QCIF=1;CIF=1
a=inactive

Step 3: ACK message is being sent back via the PBX (UDP message, Softphone -> PBX)

ACK sip:9999@192.168.112.215:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.112.215:6848;branch=z9hG4bKe951e75a-8da2-4c4f-9440-
3300f4166389;rport
To: <sip:9999@192.168.112.215>;tag=dxrgsygo
From: "1000"<sip:1000@192.168.112.215>;tag=llgsishn
CSeq: 4 ACK
Call-ID: fvlwmncqufangggiqyakimoxupwqgjnujjbttehrraqckdvgrj
Max-Forwards: 70
Contact: <sip:1000@192.168.112.215:6848>
User-Agent: Ozeki VoIP SIP SDK v10.1.8
Content-Length: 0

Related Pages

More information