Search code examples
c++pjsippjsua2

Making calls in PJSUA2


I am new to PJSUA2, and I'm trying to make calls using this library. I already managed to authorize on remote sip server, but calling is more difficult. So, here is the code:

#include <iostream>
#include <pjsua2.hpp>
#include <pjsua2/media.hpp>

using namespace std;
using namespace pj;

bool active = true;
//AudioMedia *aud_med;

class MyAccount : public Account
{
public:
    vector<Call *> calls;

MyAccount()
{}

~MyAccount()
{
    std::cout << "======= Account is being deleted: No of calls = " << calls.size() << std::endl;

    for (vector<Call *>::iterator it = calls.begin(); it != calls.end(); )
    {
        delete (*it);
        it = calls.erase(it);
    }
}

void removeCall(Call *call)
{
    for (vector<Call *>::iterator it = calls.begin();
         it != calls.end(); ++it)
    {
        if (*it == call) {
            calls.erase(it);
            break;
        }
    }
}

virtual void onRegState(OnRegStateParam &prm)
{
    AccountInfo ai = getInfo();
    std::cout << (ai.regIsActive? "*** Register:" : "*** Unregister:")
              << " code=" << prm.code << std::endl;
}
};

class MyAudioPlayer : public AudioMediaPlayer
{
public:
    MyAudioPlayer() : AudioMediaPlayer()
{

}

~MyAudioPlayer()
{

}

void onEof2()
{
    cout << "======= End of file reached" << endl;
    //this->stopTransmit(*aud_med);
    active = false;
}
};

//MyAudioPlayer player;

class MyCall : public Call
{
private:
    MyAccount *myAcc;
    MyAudioPlayer *wav_player;
public:
    MyCall(Account &acc, int callId = PJSUA_INVALID_ID) : Call(acc, callId)
    {
    myAcc = (MyAccount *)&acc;
    wav_player = NULL;
}

~MyCall()
{
    if (wav_player)
    {
        delete wav_player;
        cout << "======= Player deleted!" << endl;
    }

}

void onCallState(OnCallStateParam &state_param)
{
    CallInfo info = getInfo();

    cout << "======= CallState is: " << info.stateText << " =======" << endl;

    if(info.state == PJSIP_INV_STATE_CONFIRMED)
    {

    }
    else if (info.state == PJSIP_INV_STATE_DISCONNECTED)
    {
        myAcc->removeCall(this);
        //wav_player->stopTransmit(*aud_med);
        active = false;
        //delete wav_player;
        //delete this;
    }
}

void onCallMediaState(OnCallMediaStateParam &media_param)
{
    cout << "======= OnCallMediaState is called! =======" << endl;

    CallInfo ci = getInfo();

    if (ci.state == PJSIP_INV_STATE_CONFIRMED)
    {
        AudioMedia aud_med;

        AudioMedia& play_dev_med = Endpoint::instance().audDevManager().getPlaybackDevMedia();

        /*
        for (unsigned i = 0; i < ci.media.size(); i++)
        {
            if (ci.media[i].type == PJMEDIA_TYPE_AUDIO && getMedia(i))
            {
                aud_med = (AudioMedia *)getMedia(i);
                //aud_med.startTransmit(play_dev_med);
                break;
            }
        }
        */

        try
        {
            // Get the first audio media
            aud_med = getAudioMedia(-1);
            std::cout << "======= Got audio media!" << std::endl;
        }
        catch(...)
        {
            std::cout << "======= Failed to get audio media" << std::endl;
            return;
        }


        if (!wav_player)
        {
            wav_player = new MyAudioPlayer();

            try
            {
                wav_player->createPlayer("input.wav", PJMEDIA_FILE_NO_LOOP);
                wav_player->adjustRxLevel(3.0);
                cout << "======= Created wav player" << endl;
            }
            catch (...)
            {
                cout << "======= Failed opening wav file" << endl;
                delete wav_player;
                wav_player = NULL;
            }
        }

        aud_med.startTransmit(play_dev_med);

        if (wav_player)
            wav_player->startTransmit(aud_med);
    }
}

virtual void onCallReplaced(OnCallReplacedParam &prm)
{
    prm.newCall = new MyCall(*myAcc, prm.newCallId);
}

virtual void onCallTransferRequest(OnCallTransferRequestParam &prm)
{
    prm.newCall = new MyCall(*myAcc);
}
};

int main()
{
Endpoint ep;

ep.libCreate();

// Initialize endpoint
EpConfig ep_cfg;
ep_cfg.logConfig.level = 6;
ep.libInit( ep_cfg );

// Create SIP transport. Error handling sample is shown
TransportConfig tcfg;
tcfg.port = 0;
try
{
    ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg);
}
catch (Error &err)
{
    cout << err.info() << endl;
    return 1;
}

// Start the library (worker threads etc)
ep.libStart();
cout << "*** PJSUA2 STARTED ***" << endl;

// Configure an AccountConfig
AccountConfig acfg;
acfg.idUri = "sip:[account1]@[server]";
acfg.regConfig.registrarUri = "sip:[server]";
AuthCredInfo cred("digest", "*", "[account1]", 0, "[password]");
acfg.sipConfig.authCreds.push_back( cred );

// Create the account
MyAccount *acc = new MyAccount;
try
{
    acc->create(acfg);
}
catch(Error &err)
{
    cout << "The error is: " << err.info() << endl;
    return 1;
}

pj_thread_sleep(1500);

string call_uri = "sip:[callee]@[server]";

cout << endl;
cout << "======= Destination uri: " << call_uri << endl;
cout << endl;

Call *call = new MyCall(*acc);
acc->calls.push_back(call);
CallOpParam prm(true);
prm.opt.audioCount = 1;
prm.opt.videoCount = 0;
call->makeCall(call_uri, prm);

pj_thread_sleep(4000);
cout << "======= Cycle ended" << endl;

ep.hangupAllCalls();
cout << "======= All calls hanged up" << endl;
// Here we don't have anything else to do...
pj_thread_sleep(4000);

// Delete the account. This will unregister from server
delete acc;

// This will implicitly shutdown the library
return 0;
}

Here I am creating instance of MyAccount with credentials (these are correct), and trying to make a call. And after that call is deleted ('cause callee is not authorized, so I get PJSIP_INV_STATE_DISCONNECTED), but account can't be deleted. It writes "Deleting account 0" and does nothing else (like it stuck on some infinite cycle in MyAccount destructor). But if I won't make a call (comment line with makeCall), account is deleting just fine. I am out of options about this issue. Please help.


Solution

  • I managed to figure it out, so I'll leave an answer if anyone has that problem. Turns out call was hanging up in a wrong way (or wasn't at all, dunno). To fix that, I changed my onCallState callback, it should be like this:

    void onCallState(OnCallStateParam &state_param)
    {
        CallInfo info = getInfo();
    
        cout << "======= CallState is: " << info.stateText << " =======" << endl;
    
        if(info.state == PJSIP_INV_STATE_CONFIRMED)
        {
            // do stuff
        }
        else if (info.state == PJSIP_INV_STATE_DISCONNECTED)
        {
            // do stuff related to call disconnection
            myAcc->removeCall(this);
            delete this;
            return;
        }
    }
    

    Just need to delete call instance in the end of callback (delete this) and then return. That is the correct way to hangup call, so that thread, related to that call, was also properly deleted.