Tutorial – Project 2

Now let’s see together how easy can it be to write a simple server that automatically answers to incoming calls using APIs provided by VDK. For every incoming call, the server will answer playing a WAV audio file.

This time we will realize the server without associating to it a graphic interface.

In the second part of this tutorial, we will show you that, in less than 100 lines of code, we are able to do it!

Let’s begin showing the constructor method:

	myServer::myServer(): m_filename("vdktestingmachine.wav"){
	vdk= new VDKQtEngine();
	int result= vdk->init();
	if(!result)
		qDebug("%s",vdk->getErrorMessage(result).c_str());
	connect(vdk,SIGNAL(sigIncomingAudioCall(int,const char*,const char*)),this,SLOT(incomingCall(int,const char*,const char*)));
	connect(vdk,SIGNAL(sigFileReadingFinished(int)), this, SLOT(stopCall(int)));
	connect(vdk,SIGNAL(sigCallFinished(int)), this, SLOT(callFinished(int)));
	vdk->setActiveSipAccount(1);
	}

As you can see, it is really simple, and it consists in few lines.

m_filename is a QString variable that holds the name of the file that will be played back by the the server whenever it answers to an incoming call.

First of all, we create a VDKQtEngine object called vdk, then we initialize the internal vdk’s structure by invoking init() function, as was already explained in tutorial one documentation page.

	vdk= new VDKQtEngine();
	int result=vdk->init();
	if(!result)
		qDebug("%s",vdk->getErrorMessage(result).c_str());

Note that init() function, when executed correctly returns 0. We hold the value returned by init() function into the variable result, so that we can check if the function was executed correctly. If that wasn’t the case, we use the getErrorMessage function, that accepts as parameter an int value (that we got as init()’s result) and returns a std::string that describes what kind of error has occurred.

After that, with the instruction

	vdk->setActiveSipAccount(1);

we make active the first account that has been previously indicated inside our configuration file vdk.ini, that must be contained inside the same folder where application runs.

Moreover, we decide which functions have to be invoked when vdk emits the sigIncomingAudioCall,FileReadingFinished and sigCallFinished signals. As names suggest, these signals respectively indicate that a call is arrived and is waiting to be answered, that an audio file finished playing and that the remote party ended an established call (hung up).

	connect(vdk,SIGNAL(sigIncomingAudioCall(int,const char*,const char*),this,SLOT(incomingCall(int,const char*,const char*)));
	connect(vdk,SIGNAL(sigFileReadingFinished(int)), this, SLOT(stopCall(int)));
	connect(vdk,SIGNAL(sigCallFinished(int)), this, SLOT(callFinished(int)));

We have associated the signals to callIncoming, stopCall, callFinished slots, the only functions necessary to have the correct behaviour of our automatic responder.

Let’s see now what happens when a new call arrives:

	void
	myServer::incomingCall(int line, const char* caller, const char* callee){
	deviceId_t didFile=vdk->getFileInputDevice(m_filename.toAscii().data());
	vdk->answerAudioCall(line);
	vdk->connectDeviceToLine(didFile,line);
	m_map.insert(line, didFile);
	}

The line parameter holds the network line on which the call arrives. VDK’s sigIncomingAudioCall allows us also to know who made the call and who is the receiver for that call: in fact we have two strings, caller that holds the From field of the SIP message arrived, and callee that holds the To field.

We create a new deviceId_t for the input of our server and we associated to it the file specified in the constructor method’s initialization by invoking getFileInputDevice function.

Note that it is not necessary to define an output audio device since our server is not intended to playback the audio on external loudspeakers.

After that we anwer to the call simply by calling function answerAudioCall, that takes as argument the line of the incoming call and we let the audio stream flow through the line, by means of the connectDeviceToLine function, passing as arguments the deviceId_t associated to the input file and the network line we want to use.

From this moment on, the client who made the call begins to hear the audio file played by the server.

Last but not least, we have a map where lines for incoming calls are the keys and the associated deviceId_t are stored. We use the QMap m_map to hold these couples line-deviceId_t: this will be important later, when we will have to close an established call, as we want to handle multiple calls with this server.

When the audio file reaches the end, vdk emits the sigFileReadingFinished signal and, thanks to our code above, slot stopCall is executed:

void
	myServer::stopCall(int deviceId){
	QMap::iterator it;
	for(it=m_map.begin();it!=m_map.end();it++){
		if(it.value()==deviceId){
			vdk->disconnectDeviceFromLine(it.value(),it.key());
			vdk->stopEstablishedAudioCall(it.key());
			m_map.remove(it.key());
			break;
		}
	}
}

The deviceId parameter indicates which file has been finished playing. Thanks to m_map, we can easily find which was the network line associated to that audio file. At this point, we still have to do two things: disconnect the device from the associated line, done by calling disconnectLineFromDevice(deviceId_t, lineId_t), and stop the audio call by invoking abortCall(lineId_t) function.

Finally we remove from m_map the entry associated to the stopped call.

Now let’s see what happens when the client who started the call stops it before the file ends. In this case, vdk emits a sigCallFinished so that slot callFinished is executed:

void
	myServer::callFinished(int lineid){
	QMap::iterator it;
		for(it=m_map.begin();it!=m_map.end();it++){
			if(it.key()==lineid){
				vdk->disconnectDeviceFromLine(it.value(),it.key());
				m_map.remove(it.key());
				break;
			}
		}
	}

This function takes as argument the identifier of the network line where the stopped call was previously established (lineid). Once again, we declare a QMap::iterator to scan the m_map and to find in this way the deviceId_t associated to lineid. After that, we first stop the audio flow going on the lineid by calling disconnectDeviceFromLine(deviceId_t,lineId_t).

Once again, we also remove the item associated to line lineid.

That’s all folks, our automatic responder is ready!