1-2 次调用后,pjsua2 在 makeCall() 处挂起

pjsua2 hangs at makeCall() after 1-2 calls

我开始用 Qt5 编写应用程序,Im 运行 pjsua2_demo.cpp 来自 pjproject-2.9/pjsip-apps/src/samples/ 在一个带有无限 while(1) 循环的 QThread 中离开 pjsua2 运行永远。

当我在此处的 makeCall() 函数中进行 1-2 次调用时 blocks/hangs:

call->makeCall(num.toStdString(), prm);

另外我想知道我写的代码是否正确。 :)

这是我的mainwindow.cpp

#include "./ui_mainwindow.h"
#include "pjsuathread.h"

PjsuaThread *pjsua_thread;

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    pjsua_thread = new PjsuaThread(this);
    connect(pjsua_thread, &PjsuaThread::resultReady, this, &MainWindow::handleResults);
    connect(pjsua_thread, &PjsuaThread::resultReady2, this, &MainWindow::handleResults2);
    connect(pjsua_thread, &PjsuaThread::finished, pjsua_thread, &QObject::deleteLater);
    pjsua_thread->start();

    ui->answerButton->setVisible(false);
    ui->hangButton->setVisible(false);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::handleResults(const QString &s)
{
    ui->textEdit->setText(s);
}

void MainWindow::handleResults2(const QString &ss)
{
    if(ss=="ringing")
    {
        ui->answerButton->setVisible(true);
        ui->hangButton->setVisible(true);
    }
}


void MainWindow::on_callButton_clicked()
{
    pjsua_thread->number = "sip:2010@192.168.0.44";
    pjsua_thread->state = "call";
    ui->hangButton->setVisible(true);
}


void MainWindow::on_callButton2_clicked()
{
    pjsua_thread->number = "sip:1001@192.168.0.44";
    pjsua_thread->state = "call";
    ui->hangButton->setVisible(true);
}


void MainWindow::on_hangButton_clicked()
{
    pjsua_thread->state = "hangup";
    ui->hangButton->setVisible(false);
    ui->answerButton->setVisible(false);
}


void MainWindow::on_answerButton_clicked()
{
    pjsua_thread->state = "answer";
    ui->hangButton->setVisible(true);
    ui->answerButton->setVisible(false);
}

这是我的pjsuathread.cpp

#include <QString>

#include "pjsuathread.h"
#include "pjsua2_main.h"

PjsuaThread *pjt = NULL;

PjsuaThread::PjsuaThread(QObject *parent)
    : QThread{parent}
{
    pjt = this;
}

void PjsuaThread::run()
{
    pjsua2_main();
}

这是我的 pjsua2_main.cpp

#include "pjsua2_main.h"

#include <pjsua2.hpp>
#include <iostream>
#include <pj/file_access.h>

#define THIS_FILE   "pjsua2_main.cpp"

using namespace pj;

Call *call = NULL;

class MyAccount;

class MyCall : public Call
{
private:
    MyAccount *myAcc;

public:
    MyCall(Account &acc, int call_id = PJSUA_INVALID_ID)
    : Call(acc, call_id)
    {
        myAcc = (MyAccount *)&acc;
    }

    ~MyCall()
    {
    }

    virtual void onCallState(OnCallStateParam &prm);
    virtual void onCallTransferRequest(OnCallTransferRequestParam &prm);
    virtual void onCallReplaced(OnCallReplacedParam &prm);
    virtual void onCallMediaState(OnCallMediaStateParam &prm);
};

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

public:
    MyAccount()
    {}

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

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

    void removeCall(Call *call)
    {
        for (std::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: code=" : "*** Unregister: code=")
          << prm.code << std::endl;
    }

    virtual void onIncomingCall(OnIncomingCallParam &iprm)
    {
        call = new MyCall(*this, iprm.callId);
        CallInfo ci = call->getInfo();

        std::cout << "*** Incoming Call: " <<  ci.remoteUri << " ["
                  << ci.stateText << "]" << std::endl;

        emit pjt->resultReady2("ringing");

        calls.push_back(call);
    }
};

void MyCall::onCallState(OnCallStateParam &prm)
{
    PJ_UNUSED_ARG(prm);

    CallInfo ci = getInfo();
    std::cout << "*** Call: " <<  ci.remoteUri << " [" << ci.stateText << "]" << std::endl;

    pjt->info+="*** Call: ";
    pjt->info+=QString::fromStdString(ci.remoteUri);
    pjt->info+=" [";
    pjt->info+=QString::fromStdString(ci.stateText);
    pjt->info+="]\n";
    pjt->infoChanged=true;

    if (ci.state == PJSIP_INV_STATE_DISCONNECTED)
    {
        //pjt->remove_call=true;
        myAcc->removeCall(this);
        /* Delete the call */
        delete this;
    }
}

void MyCall::onCallMediaState(OnCallMediaStateParam &prm)
{
    //PJ_UNUSED_ARG(prm);

    CallInfo ci = getInfo();
    AudioMedia aud_med;

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

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

    pjt->info+="*** Connecting audio streams *** \n";
    pjt->infoChanged=true;

    // This will connect the sound device/mic to the call audio media
    cap_dev_med.startTransmit(aud_med);

    // And this will connect the call audio media to the sound device/speaker
    aud_med.startTransmit(play_dev_med);
}

void MyCall::onCallTransferRequest(OnCallTransferRequestParam &prm)
{
    /* Create new Call for call transfer */
    prm.newCall = new MyCall(*myAcc);
}

void MyCall::onCallReplaced(OnCallReplacedParam &prm)
{
    /* Create new Call for call replace */
    prm.newCall = new MyCall(*myAcc, prm.newCallId);
}

MyAccount *acc(new MyAccount);

void make_call(QString num)
{
    if(pjt->remove_call)
    {
        acc->removeCall(call);
        /* Delete the call */
        delete call;

        pjt->remove_call=true;
    }
    std::cout << "here 1" << std::endl;
    // Make outgoing call
    call = new MyCall(*acc);
    std::cout << "here 2" << std::endl;
    acc->calls.push_back(call);
    std::cout << "here 3" << std::endl;
    CallOpParam prm(true);
    std::cout << "here 4" << std::endl;
    prm.opt.audioCount = 1;
    prm.opt.videoCount = 0;
    std::cout << "here 5" << std::endl;
    call->makeCall(num.toStdString(), prm);
    std::cout << "here 6" << std::endl;
}

static void mainProg1(Endpoint &ep)
{
    // Init library
    EpConfig ep_cfg;
    ep_cfg.logConfig.level = 4;
    ep_cfg.medConfig.threadCnt = 2;
    ep.libInit( ep_cfg );

    // Transport
    TransportConfig tcfg;
    tcfg.port = 5080;
    ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg);

    // Start library
    ep.libStart();
    std::cout << "*** PJSUA2 STARTED ***" << std::endl;

    pjt->info="*** PJSUA2 STARTED ***\n";
    emit pjt->resultReady(pjt->info);

    // Add account
    AccountConfig acc_cfg;
    acc_cfg.idUri = "sip:2033@192.168.0.44";
    acc_cfg.regConfig.registrarUri = "sip:192.168.0.44";
    acc_cfg.sipConfig.authCreds.push_back( AuthCredInfo("digest", "*", "2033", 0, "PAss1234") );

    try {
    acc->create(acc_cfg);
    } catch (...) {
    std::cout << "Adding account failed" << std::endl;
    }

    //pj_thread_sleep(2000);
    //make_call();

    while(1)
    {
        if(pjt->state=="call")
        {
            pjt->state="";

            std::cout << "############################# CALL ############################" << std::endl;

            make_call(pjt->number);

            std::cout << "###############################################################" << std::endl;
        }
        else if(pjt->state=="answer")
        {
            pjt->state="";
            CallOpParam prm;
            prm.statusCode = (pjsip_status_code)200;
            call->answer(prm);
        }
        else if(pjt->state=="hangup")
        {
            pjt->state="";
            CallOpParam prm;
            prm.statusCode = (pjsip_status_code)486;
            call->hangup(prm);
            //ep.hangupAllCalls();
        }
        else if(pjt->infoChanged)
        {
            pjt->infoChanged=false;
            emit pjt->resultReady(pjt->info);
        }
        else if(pjt->remove_call)
        {
            pjt->info+="Removing call...\n";
            pjt->infoChanged=true;

            acc->removeCall(call);
            /* Delete the call */
            delete call;

            pjt->remove_call=false;
        }

        //ep.libHandleEvents(60);
    }

    // Hangup all calls
    //    pj_thread_sleep(4000);
    //    ep.hangupAllCalls();
    //    pj_thread_sleep(4000);
    //

    // Destroy library
    std::cout << "*** PJSUA2 SHUTTING DOWN ***" << std::endl;
    delete acc; /* Will delete all calls too */
}

int pjsua2_main()
{
    int ret = 0;
    Endpoint ep;

    try {
    ep.libCreate();

    mainProg1(ep);
    ret = PJ_SUCCESS;
    } catch (Error & err) {
    std::cout << "Exception: " << err.info() << std::endl;
    ret = 1;
    }

    try {
    ep.libDestroy();
    } catch(Error &err) {
    std::cout << "Exception: " << err.info() << std::endl;
    ret = 1;
    }

    if (ret == PJ_SUCCESS) {
    std::cout << "Success" << std::endl;
    } else {
    std::cout << "Error Found" << std::endl;
    }

    return ret;
}

这些是我的头文件:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

public slots:
    void handleResults(const QString &s);
    void handleResults2(const QString &ss);
private slots:
    void on_callButton_clicked();
    void on_callButton2_clicked();
    void on_hangButton_clicked();
    void on_answerButton_clicked();
};
#endif // MAINWINDOW_H

pjsuathread.h

#ifndef PJSUATHREAD_H
#define PJSUATHREAD_H

#include <QThread>

class PjsuaThread : public QThread
{
    Q_OBJECT
public:
    explicit PjsuaThread(QObject *parent = nullptr);
    void run() override;
    QString state;
    QString info;
    QString number;
    bool infoChanged=false;
    bool ringing=false;
    bool remove_call=false;

signals:
    void resultReady(const QString &s);
    void resultReady2(const QString &ss);
};

#endif // PJSUATHREAD_H

pjsua2_main.h

#ifndef PJSUA2_MAIN_H
#define PJSUA2_MAIN_H

#include "pjsuathread.h"

int pjsua2_main();
extern PjsuaThread *pjt;

#endif // PJSUA2_MAIN_H

我解决了这个问题, 它是 pjproject 的版本。 现在我尝试使用 v2.10,一切正常。

v2.12 和 v2.11 不起作用,因为它们在我编译演示应用程序时给我未定义的引用。