在 PJSUA2 中拨打电话
Making calls in 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),帐户删除就好了。
我对这个问题没有选择。请帮忙。
我设法弄明白了,所以如果有人有这个问题,我会留下答案。
结果电话以错误的方式挂断了(或者根本没有,不知道)。为了解决这个问题,我更改了 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;
}
}
只需要在回调结束时删除调用实例(删除这个)然后return。这是挂断电话的正确方式,因此与该电话相关的线程也被正确删除。
我是 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),帐户删除就好了。 我对这个问题没有选择。请帮忙。
我设法弄明白了,所以如果有人有这个问题,我会留下答案。 结果电话以错误的方式挂断了(或者根本没有,不知道)。为了解决这个问题,我更改了 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;
}
}
只需要在回调结束时删除调用实例(删除这个)然后return。这是挂断电话的正确方式,因此与该电话相关的线程也被正确删除。