PJSUA2 pb_thread_func: underrun

  alsa, c++, pjsua2

I am trying to make a call using PJSUA2, play a wav file to the callee, and callee should record this file to wav. I created two programs for that (caller and callee). And when caller starts playing wav file into AudioMedia object, console of callee program just spams out "alsa.c pb_thread_func: underrun!" and nothing can be heard or recorded.

Here is caller main.cpp:

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

using namespace std;
using namespace pj;

bool active = true;

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

    MyAccount()
    {}

    ~MyAccount()
    {
        shutdown();
        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
{
private:
    AudioMedia audio;
public:
    MyAudioPlayer(AudioMedia aud_med) : AudioMediaPlayer()
    {
        audio = aud_med;
    }

    ~MyAudioPlayer()
    {

    }

    virtual void onEof2();
};

void MyAudioPlayer::onEof2()
{
    cout << "======= End of file reached" << endl;
    this->stopTransmit(audio);
    active = false;
    return;
}

//MyAudioPlayer player;

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

    ~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);
            active = false;
            delete this;
            return;
        }
    }

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

        CallInfo ci = getInfo();
        cout << "======= CallState is: " << ci.stateText << " =======" << endl;

        //AudioMedia aud_med;

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

        try
        {
            // Get the first audio media
            aud_med = getAudioMedia(-1);
            cout << "======= Got audio media" << endl;
        }
        catch(...)
        {
            cout << "======= Failed to get audio media" << 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;
            }
        }

        cout << "======= Playing file" << endl;
        if (wav_player)
        {
            wav_player->startTransmit(aud_med);
        }

        //aud_med.startTransmit(play_dev_med);
    }
};

int main()
{
    Endpoint ep;

    ep.libCreate();

    // Initialize endpoint
    EpConfig ep_cfg;
    ep_cfg.logConfig.level = 5;
    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:[caller]@[server]";
    acfg.regConfig.registrarUri = "sip:[server]";
    AuthCredInfo cred("digest", "*", "[caller]", 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(6000);

    ep.hangupAllCalls();
    cout << "======= All calls hanged up" << endl;

    // Here we don't have anything else to do...
    pj_thread_sleep(1000);

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

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

And here in callee main.cpp:

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

using namespace std;
using namespace pj;

bool active = true;

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

    MyAccount()
    {}

    ~MyAccount()
    {
        shutdown();
        cout << "======= Account is being deleted: No of calls = " << calls.size() << 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();
        cout << (ai.regIsActive? "*** Register:" : "*** Unregister:")
                  << " code=" << prm.code << endl;
    }

    virtual void onIncomingCall(OnIncomingCallParam &call_prm);
};

class MyCall : public Call
{
private:
    MyAccount *myAcc;
    AudioMediaRecorder *wav_recorder;
    AudioMedia aud_med;
public:
    MyCall(Account &acc, int callId = PJSUA_INVALID_ID) : Call(acc, callId)
    {
        myAcc = (MyAccount *)&acc;
        wav_recorder = NULL;
    }

    ~MyCall()
    {
        if (wav_recorder)
        {
            try
            {
                aud_med.stopTransmit(*wav_recorder);
            }
            catch(Error& err)
            {
                cout << "Error: " << err.info() << endl;
            }

            delete wav_recorder;
            cout << "======= Recorder deleted =======" << endl;
        }

    }

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

        cout << "======= OnCallState: callState is: " << info.stateText << " =======" << endl;

        if(info.state == PJSIP_INV_STATE_CONFIRMED)
        {

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

    void onCallMediaState(OnCallMediaStateParam &media_param)
    {
        CallInfo ci = getInfo();
        cout << "======= OnCallMediaState: callState is: " << ci.stateText << " =======" << endl;
        try
        {
            // Get the first audio media
            aud_med = getAudioMedia(-1);
            cout << "======= Got audio media =======" << endl;
        }
        catch(...)
        {
            cout << "======= Failed to get audio media =======" << endl;
            return;
        }

        if (!wav_recorder)
        {
            wav_recorder = new AudioMediaRecorder();

            try
            {
                wav_recorder->createRecorder("test.wav");
                cout << "======= Created wav recorder =======" << endl;
            }
            catch (...)
            {
                cout << "======= Failed opening wav file =======" << endl;
                delete wav_recorder;
                wav_recorder = NULL;
            }
        }

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

        if (wav_recorder)
        {
            cout << "======= Recording file =======" << endl;
            aud_med.startTransmit(*wav_recorder);
        }
    }
};

MyCall *incomingCall;

void MyAccount::onIncomingCall(OnIncomingCallParam &call_prm)
{
    cout << "======= OnIncomingCall is called =======" << endl;
    incomingCall = new MyCall(*this, call_prm.callId);

    CallInfo ci = incomingCall->getInfo();
    CallOpParam prm;

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

    calls.push_back(incomingCall);
    prm.statusCode = (pjsip_status_code)200;
    incomingCall->answer(prm);
}

int main()
{
    Endpoint ep;

    ep.libCreate();

    // Initialize endpoint
    EpConfig ep_cfg;
    ep_cfg.logConfig.level = 8;
    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)
    {
        std::cout << err.info() << std::endl;
        return 1;
    }

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

    // Configure an AccountConfig
    AccountConfig acfg;
    acfg.idUri = "sip:[callee]@[server]";
    acfg.regConfig.registrarUri = "sip:[server]";

    AuthCredInfo cred("digest", "*", "[callee]", 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;
    }

    // Here we don't have anything else to do...
    while(active)
    {

    }

    pj_thread_sleep(1000);
    ep.hangupAllCalls();

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

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

Audio out works fine, so this is definitely AudioMedia issue (perhaps I’m connecting it in a wrong way). Does anyone has any ideas about what is wrong?

Source: Windows Questions C++

LEAVE A COMMENT