首页 > 解决方案 > 在 PJSUA2 中拨打电话

问题描述

我是 PJSUA2 的新手,我正在尝试使用这个库进行调用。我已经设法在远程 sip 服务器上进行了授权,但是调用比较困难。所以,这里是代码:

#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;
}

在这里,我正在使用凭据创建 MyAccount 实例(这些是正确的),并尝试拨打电话。并且在该呼叫被删除之后(因为被呼叫者未被授权,所以我得到 PJSIP_INV_STATE_DISCONNECTED),但无法删除帐户。它写入“删除帐户 0”并且什么都不做(就像它卡在 MyAccount 析构函数中的某个无限循环中)。但如果我不打电话(注释行makeCall),帐户删除就好了。我对这个问题没有选择。请帮忙。

标签: c++pjsippjsua2

解决方案


我设法弄清楚了,所以如果有人有这个问题,我会留下答案。结果是电话挂断了错误的方式(或者根本没有,不知道)。为了解决这个问题,我改变了我的onCallState回调,它应该是这样的:

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;
    }
}

只需要在回调结束时删除调用实例(删除这个)然后返回。这是挂断呼叫的正确方法,因此与该呼叫相关的线程也被正确删除。


推荐阅读