Tutorial – Project 1

This is the first chapter of a tutorial on how to use the VDK, our goal will be to write a simple softphone.

The SDK provides two main classes that exports high-level APIs that are to be used within a Qt project (using Qt-specific SLOT & SIGNAL architecture) or within a normal C++ environment (registering a callback to trap the events).

These two main classes are VdkQtEngine (that use the Qt framework) and VDKEngine (the c++ standard way), for the first part of this tutorial we will focus on VDKQtEngine and create a project that will be using the Qt libraries.

The creation of a graphical interface is out of the scope of this tutorial so, we will assume to have a simple window with a textbox (the address of the callee) and two buttons, one to call and one to hangup the call. The C++ object names used will be: btnCall, btnHangup and txtDestinationAddress to identify the button that starts a call, the one to end it and the text box containing the SIP address of the callee.

Screenshot project 1

Let’s see what would be the constructor method of this test widget:

testWidget::testWidget(QWidget *parent, Qt::WFlags f)
: QWidget(parent,f)
{
  setupUi(this);

  connect(btnCall,SIGNAL(clicked()),this,SLOT(btnCallClick()));
  connect(btnHangup,SIGNAL(clicked()),this,SLOT(btnHangupClick()));

  vdk = new VDKQtEngine();
  vdk->init();

  micId = vdk->getDefaultAudioInputDevice();
  speakersId = vdk->getDefaultAudioOutputDevice();

  connect(vdk,SIGNAL(sigCallEstablished(int)),this,SLOT(startTalking(int)));
}

the lines

	setupUi(this);

	connect(btnCall,SIGNAL(clicked()),this,SLOT(btnCallClick()));
	connect(btnHangup,SIGNAL(clicked()),this,SLOT(btnHangupClick()));

are Qt specific and are used to setup the graphical interface and to register the callback functions that will be called when the user presses one of the two buttons; more specifically when the btnCall is called the btnCallClick function will be invoked while btnHangupClick will be executed when the other button is pressed.

  vdk = new VDKQtEngine();
  vdk->init();
  vdk->setActiveSipAccount(1);

We then create an object of type VDKQtEngine and call it vdk;
the first method to be used is the init() function to initialize the internal structure of the sdk. The second method (setActiveAccount) sets the sip account to be used in the current session. The ‘1′ parameter indicates that the first sip account listed in the vdk.ini file will be used.

  micId = vdk->getDefaultAudioInputDevice();
  speakersId = vdk->getDefaultAudioOutputDevice();

we ask the SDK the identifiers for the audio devices for input and output (usually these are the default microphone and speaker set defined on the Operating System) and store them in two variables for later use.

connect(vdk,SIGNAL(sigCallEstablished(int)),this,SLOT(startTalking(int)));

finally we decide that whenever the VDK Engine notifies of a callEstablished event the startTalking function will be executed.


Let’s see what happens when we click on the btnCall button

void
testWidget::btnCallClick()
{
	lineId = vdk->startAudioCall(txtDestinationAddress->text());
}

as we can see the VDK function called is pretty straightforward: we want to place an audio call to the address specified in the txtDestinationAddres text box.

Please note that we just initiated an audio call, but there is no media flow going on since the remote party didn’t answer yet.

The value returned by the function is the MoIp Line that will be used to handle this call; since there are no other calls being handled the line will be the first one and therefore will have an identifier of “0″. We store the identifier for future use.

When the remote party picks up the phone the call will pass in the established state and the VDK will emit the callEstablished signal, hence our test application will execute the startTalking function.

void
testWidget::startTalking(int linenumber)
{
	vdk->connectDeviceToLine(micId,linenumber);
	vdk->connectLineToDevice(linenumber,speakersId);
}

The linenumber parameter indicates the MoIP Line on which the call was established. These lines can be thought like virtual telephone lines on which communications take place. For the sake of this example there is no need to check the line identifier since we are just handling one call.

So, since the call has been established, we now need that the voice coming from the microphone goes over this line; so pretty strightforwardly we use the
connectDeviceToLine passing the microphone identifier micId we gathered early and the line number (linenumber) we want to send the voice on.

If we limit to this we won’t be able to hear what the remote party is saying so we also need to connect the media flow coming from this line to the speakers:
connectLineToDevice passing the identifiers of the line from which get the audio and the identifier of the speakers (speakersId) where to redirect the audio.

Great, so far we are able to perform our first VoIP call, but still we have no way to end it!

let’s examine the callback function of the btnHangup

void
testWidget::btnHangupClick()
{
	vdk->stopEstablishedAudioCall(lineId); //the linenumber is the only one we are using in this demo
	vdk->disconnectDeviceFromLine(micId,lineId);
	vdk->disconnectLineFromDevice(lineId,speakersId);
}

first we tell the remote party that the call is finished by using the stopEstablishedAudioCall function. The parameter passed is the identifier of the line we used to start the audio call and (should be) the same one where the call was established.

Then in a symetric way as we did before when the call was established we need to disconnect the microphone and speakers from the line.

There we are, we have created a simple softphone in less than fifty lines of code! pretty impressive isn’t it?


Note 1:

Remember to place the vdk.ini configuration file in your project folder or in the application folder (if you run it from command line) or your application will fail.

Note 2:

Remember to place all the libraries provided in the VDK package where your application can find it.


The entire code in one piece:
first the declaration

#ifndef __testWidget_INC__
#define __testWidget_INC__

#include "ui_phone.h"

#include <QWidget>

#include "VDKQtEngine.h"

class testWidget : public QWidget, private Ui::Form
{
	Q_OBJECT

	int micId,speakersId,lineId;
	VDKQtEngine *vdk;

public:
	testWidget(QWidget *parent=0, Qt::WFlags f=0);

public slots:
	void btnCallClick();
	void btnHangupClick();
	void startTalking(int);
};
#endif

then the definition

#include "testWidget.h"

testWidget::testWidget(QWidget *parent, Qt::WFlags f)
: QWidget(parent,f)
{
	setupUi(this);

	connect(btnCall,SIGNAL(clicked()),this,SLOT(btnCallClick()));
	connect(btnHangup,SIGNAL(clicked()),this,SLOT(btnHangupClick()));

	vdk = new VDKQtEngine();
	vdk->init();
	vdk->setActiveSipAccount(1);

	micId = vdk->getDefaultAudioInputDevice();
	speakersId = vdk->getDefaultAudioOutputDevice();

	connect(vdk,SIGNAL(sigCallEstablished(int)),this,SLOT(startTalking(int)));
}

void
testWidget::btnCallClick()
{
	lineId = vdk->startAudioCall(txtDestinationAddress->text());
}

void
testWidget::startTalking(int linenumber)
{
	vdk->connectDeviceToLine(micId,linenumber);
	vdk->connectLineToDevice(linenumber,speakersId);
}

void
testWidget::btnHangupClick()
{
	vdk->stopEstablishedAudioCall(0); //the linenumber is the only one we are using in this demo
	vdk->disconnectDeviceFromLine(micId,0);
	vdk->disconnectLineFromDevice(0,speakersId);
}

//That's all folks, the softphone is ready!