c++ - 在 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;
}
}
只需要在回调结束时删除调用实例(删除这个)然后返回。这是挂断呼叫的正确方法,因此与该呼叫相关的线程也被正确删除。
推荐阅读
- mysql - Postgres 在“AT”处或附近触发事件语法错误
- python - 使用 Django 模型存储每个零售商和产品的产品 URL
- asp.net-core - EF Core 中使用 FluentAPI 的一对一关系
- uwp - 哪些通用 Windows 应用支持 LaunchUriForResultsAsync?
- sql - 将最大值计算列添加到 Where 子句?
- node.js - 如何修复放大 CLI 错误:“-bash:放大:找不到命令”
- acumatica - 如何将新筛选字段添加到按项目运行分配屏幕
- r - 如何在 R 中使用 fportfolio 包进行非时间序列输入?
- php - 从 RSS 文件中提取节点
- java - 关于初始化 bean 和属性的错误