Tutorial – Project 3
Now, we’ll examine the third example: this time we will realize a new softphone, very similar to that already shown in the first tutorial, but with an extra feature: with this application the user will be able, after having established a new call, to stop audio input coming from the microphone and replace it with a selected input WAV audio file.
The application’s graphic interface is very simple, in fact we need only a text box, called textAddress, used to insert the SIP address of the callee, and three different buttons: the first one, called btnCall, to start a new call, the second, called btnHangup, to stop it, the last, called btnHold, that is used to switch the audio input from microphone to WAV file and vice versa.
As always, let’s begin from the constructor method:
OnHoldPhone::OnHoldPhone(QWidget *parent, Qt::WFlags flags) : QWidget(parent, flags), filename("vdktestingmachine.wav"), call_on(false), hold_on(false) { setupUi(this); vdk = new VDKQtEngine(); vdk->init(); vdk->setActiveSipAccount(1); btnCall->setEnabled(true); btnHangup->setEnabled(false); btnOnHold->setEnabled(false); connect(vdk,SIGNAL(sigCallEstablished(int)), this,SLOT(onCallEstablished(int))); connect(vdk,SIGNAL(sigCallFinished(int)),this,SLOT(onCallFinished(int))); connect(btnCall,SIGNAL(clicked()),this,SLOT(btnCallclicked())); connect(btnHangup,SIGNAL(clicked()),this,SLOT(btnHangupclicked())); connect(btnOnHold,SIGNAL(clicked()),this,SLOT(btnOnHoldclicked())); }
We have two bool variables that indicate respectively if a call is established or not (call_on) and if we have switched to the WAV input audio file or we are using microphone as device for audio input (hold_on). Moreover, we have a QString variable that holds the name of the file to be played when the call is put on hold.
The instructions:
setupUi(this); connect(btnCall,SIGNAL(clicked()),this,SLOT(btnCallclicked())); connect(btnHangup,SIGNAL(clicked()),this,SLOT(btnHangupclicked())); connect(btnOnHold,SIGNAL(clicked()),this,SLOT(btnOnHoldclicked()));
are used to setup the graphic interface and to invoke the appropriate function when a button is pressed.
vdk = new VDKQtEngine(); vdk->init(); vdk->setActiveSipAccount(1);
At first, like usually done, we declare a VDKQtEngine object, calling it vdk, and we call init() function. After that we call setActiveAccount, making active the first SIP account inserted in vdk.ini.
This part has been thouroughly described in the first two tutorials, so if it you have doubts about it please be sure to check part 1 and 2 of the tutorial.
The lines:
btnCall->setEnabled(true); btnHangup->setEnabled(false); btnOnHold->setEnabled(false);
are Qt specific; they prevent the user to try to close or to put on hold a call before having established it. This is done to keep the GUI in a coherent state.
Finally, we decide that if a vdk’s signal sigCallEstablished is emitted then the slot onCallEstablished must be invoked; likewise, we call onCallFinished every time a vdk’s signal sigCallFinished is emitted.
connect(vdk,SIGNAL(sigCallEstablished(int)),this,SLOT(onCallEstablished(int))); connect(vdk,SIGNAL(sigCallFinished(int)),this,SLOT(onCallFinished(int)));
We have finished with the constructor method, so let’s get on to member functions.
When btnCall is pressed, as already seen above, slot btnCallclicked() is called. What will it happen?
void OnHoldPhone::btnCallclicked() { voipLineId = vdk->startAudioCall(destAddress->text().toAscii().data()); call_on=true; btnHangup->setEnabled(true); btnCall->setEnabled(false); }
Simple, isn’t it? We just call vdk’s startAudioCall function, starting in this way a call to the callee address that is specified inside destAddress text box. As you may remember, this function returns the value of the network line through which the call will take place; this value will be necessary afterwards, so we store it in the voipLineId variable.
Then we set the call_on variable to remind that a call is in progress and, finally, we disable btnCall and enable btnHangup.
No audio flow is going through the line, since no one has accepted our call yet! When it happens, vdk emits a sigCallEstablished that will fire up the onCallEstablished function:
void OnHoldPhone::onCallEstablished(int line) { micId= vdk->getDefaultAudioInputDevice(); spkId = vdk->getDefaultAudioOutputDevice(); vdk->connectDeviceToLine(micId,line); vdk->connectLineToDevice(line,spkId); btnOnHold->setEnabled(true); }
The function takes as its argument an int value that represents the line which the sigCallEstablished is related to.
First of all, it gets the identifiers for audio input and output devices and stores them into micId and spkId variables, then it lets the audio flow start in both directions, calling in sequence connectDeviceToLine and connectLineToDevice functions, passing as arguments the deviceId_t previously set and the line line. In the end, it enables btnOnHold, ’cause from this moment on it will be possible to switch the audio input from microphone to the audio file.
Let’s see now what happens when the user clicks on btnOnHold and function btnOnHoldClicked() is invoked:
void OnHoldPhone::btnOnHoldclicked() { vdk->disconnectDeviceFromLine(micId,voipLineId); if(!hold_on){ micId=vdk->getFileInputDevice(filename.toAscii().data(),true); btnOnHold->setText(QString("Speak again")); hold_on=true; } else { micId=vdk->getDefaultAudioInputDevice(); btnOnHold->setText(QString("On Hold")); hold_on=false; } vdk->connectDeviceToLine(micId,voipLineId); }
The function, before all, invokes the disconnectDeviceFromLine vdk’s API, stopping the audio flow going from the input device to the network line involved in the call. Remember that we know the line identifier since we have stored it in voipLineId variable when startAudioCall API (in btnCallClicked() method) has been executed.
After that, there are two possible situations: we were using microphone as audio input device, or we are taking the audio flow from a WAV audio file. We can discern between these cases thanks to hold_on variable: in the first case, in fact, hold_on is false, otherwise it is true.
So if the microphone is our input audio device the code lines:
micId=vdk->getFileInputDevice(filename.toAscii().data(),true); btnOnHold->setText(QString("Speak again")); hold_on=true;
will be executed. So, vdk’s getFileInputDevice is called: this time we pass to it two arguments, the filename variable, and the bool value true; the latter one indicates that the input file has to be read in a loop, and the default value for the function is false. Then we change the btnOnHold text to “Speak again”, to make more intuitive our graphic interface and we set the hold_on variable.
In the other case, the following code is executed:
micId=vdk->getDefaultAudioInputDevice(); btnOnHold->setText(QString("On Hold")); hold_on=false;
With this we associate the stardard input audio device defined by the operating system to the micId deviceId_t, we change the text on btnOnHold to “On Hold” and we unset hold_on variable.
We are almost ready, we only need to define what happens when the call ends. There are two possible scenarios: we can choose to end the call, by clicking on btnHangup, or the user on the other side of the line can stop it before us.
When we click on btnHangup, slot btnHangupClicked() is called, as you can see in the constructor above.
void OnHoldPhone::btnHangupclicked() { vdk->disconnectDeviceFromLine(micId,voipLineId); vdk->disconnectLineFromDevice(voipLineId,spkId); vdk->abortCall(voipLineId); call_on=false; hold_on=false; btnOnHold->setText(QString("On Hold")); btnCall->setEnabled(true); btnHangup->setEnabled(false); btnOnHold->setEnabled(false); }
First of all, we disconnect the audio flow input and output from the line. Then we call the abortCall(lineId_t) to stop the call. Why don’t we invoke stopEstablishedAudioCall as we did in the last example? This is because we don’t know if the the call we want to interrupt is already established, or if we are stopping it before remote party has answered; however this is not a problem, since abortCall manages both situations, so we don’t have to worry about it.
After that we unset call_on and hold_on variables, and set the btnOnHold’s text to “On Hold”; finally we re-enable btnCall and disable btnHangup and btnOnHold.
In case that the client on the other network line’s side decides to stop the established call, vdk emits sigCallFinished and slot onCallFinished(line) is called:
void OnHoldPhone::onCallFinished(int line) { vdk->disconnectDeviceFromLine(micId,line); vdk->disconnectLineFromDevice(line,spkId); btnCall->setEnabled(true); btnHangup->setEnabled(false); btnOnHold->setEnabled(false); btnOnHold->setText(QString("On Hold")); call_on=false; hold_on=false; }
It is almost equal to the last function, except for the abortCall that in this case we don’t need to call, because the call has already been stopped.
So the softphone is finished , we have just to try it!