pjsipua-win 0.1.3

Rust library PJSUA2
// src/pjsua2_wrapper.cpp - Fixed version with proper callbacks
#include <pjsua2.hpp>
#include <pjsua-lib/pjsua.h>
#include <iostream>
#include <memory>
#include <atomic>
#include <mutex>
#include <map>
#ifdef _WIN32
#include <windows.h>
#endif
#include "pjsua2_wrapper.h"

using namespace pj;

namespace {
    std::atomic<bool> g_initialized{false};
    std::unique_ptr<Endpoint> g_endpoint;
    std::mutex g_mutex;
    std::map<int, std::shared_ptr<Account>> g_accounts;
    std::map<int, std::shared_ptr<Call>> g_calls;
    std::atomic<int> g_next_account_id{0};

    // Global callback function pointers
    void (*g_on_reg_state)(int acc_id, int is_registered) = nullptr;
    void (*g_on_incoming_call)(int acc_id, int call_id, const char* remote_uri) = nullptr;
    void (*g_on_call_state)(int call_id, int state) = nullptr;
    void (*g_on_call_media_state)(int call_id, int state) = nullptr;
}

// Custom Account class with callback support
class MyAccount : public Account {
private:
    int m_id;

public:
    MyAccount(int id) : m_id(id) {}

    virtual ~MyAccount() {}

    // Override registration state callback
    virtual void onRegState(OnRegStateParam &prm) {
        std::cout << "[PJSUA2] Account " << m_id << " registration state changed\n";

        AccountInfo ai = getInfo();
        bool registered = ai.regIsActive;

        std::cout << "[PJSUA2] Registration status: " << (registered ? "registered" : "not registered") << "\n";

        // Call the global callback if set
        if (g_on_reg_state) {
            std::cout << "[PJSUA2] Calling registration callback for account " << m_id << "\n";
            g_on_reg_state(m_id, registered ? 1 : 0);
        }
    }

    // Override incoming call callback
    virtual void onIncomingCall(OnIncomingCallParam &iprm) {
        std::cout << "[PJSUA2] Incoming call to account " << m_id << "\n";

        Call *call = new Call(*this, iprm.callId);
        int call_id = call->getId();
        CallInfo ci = call->getInfo();

        g_calls[call_id] = std::shared_ptr<Call>(call);

        if (g_on_incoming_call) {
            g_on_incoming_call(m_id, call_id, ci.remoteUri.c_str());
        }
    }
};

extern "C" {

int pjsua2_create_endpoint() {
    std::lock_guard<std::mutex> lock(g_mutex);

    if (g_initialized.load()) {
        return 0;
    }

    try {
        g_endpoint = std::make_unique<Endpoint>();
        g_endpoint->libCreate();
        g_initialized.store(true);
        std::cout << "[PJSUA2] Endpoint created\n";
        return 0;
    } catch (Error& err) {
        std::cerr << "[PJSUA2] Error: " << err.reason << "\n";
        return -1;
    }
}

int pjsua2_init_endpoint(int log_level) {
    std::lock_guard<std::mutex> lock(g_mutex);

    if (!g_initialized.load() || !g_endpoint) {
        return -1;
    }

    try {
        EpConfig cfg;
        cfg.logConfig.level = log_level;
        cfg.logConfig.consoleLevel = log_level;

        #ifdef _WIN32
        char tempPath[MAX_PATH];
        if (GetTempPathA(MAX_PATH, tempPath)) {
            cfg.logConfig.filename = std::string(tempPath) + "pjsua2_test.log";
        }
        #endif

        cfg.uaConfig.userAgent = "PJSUA2-Test/1.0";
        cfg.uaConfig.maxCalls = 4;

        g_endpoint->libInit(cfg);
        std::cout << "[PJSUA2] Endpoint initialized\n";
        return 0;
    } catch (Error& err) {
        std::cerr << "[PJSUA2] Error: " << err.reason << "\n";
        return -1;
    }
}

int pjsua2_create_transport(int port) {
    std::lock_guard<std::mutex> lock(g_mutex);

    if (!g_initialized.load() || !g_endpoint) {
        return -1;
    }

    try {
        TransportConfig tcfg;
        tcfg.port = port;
        g_endpoint->transportCreate(PJSIP_TRANSPORT_UDP, tcfg);
        std::cout << "[PJSUA2] Transport created on port " << port << "\n";
        return 0;
    } catch (Error& err) {
        std::cerr << "[PJSUA2] Error: " << err.reason << "\n";
        return -1;
    }
}

int pjsua2_start() {
    std::lock_guard<std::mutex> lock(g_mutex);

    if (!g_initialized.load() || !g_endpoint) {
        return -1;
    }

    try {
        g_endpoint->libStart();
        std::cout << "[PJSUA2] Started\n";
        return 0;
    } catch (Error& err) {
        std::cerr << "[PJSUA2] Error: " << err.reason << "\n";
        return -1;
    }
}

void pjsua2_destroy() {
    std::lock_guard<std::mutex> lock(g_mutex);

    if (!g_initialized.load() || !g_endpoint) {
        return;
    }

    try {
        g_accounts.clear();
        g_calls.clear();
        g_endpoint->libDestroy();
        g_endpoint.reset();
        g_initialized.store(false);
        std::cout << "[PJSUA2] Destroyed\n";
    } catch (...) {
        g_endpoint.reset();
        g_initialized.store(false);
    }
}

int pjsua2_create_account_simple(const char* id_uri, const char* registrar_uri,
                                const char* username, const char* password) {
    std::lock_guard<std::mutex> lock(g_mutex);

    if (!g_initialized.load() || !g_endpoint) {
        return -1;
    }

    try {
        AccountConfig cfg;
        cfg.idUri = id_uri;
        cfg.regConfig.registrarUri = registrar_uri;

        AuthCredInfo cred("digest", "*", username, 0, password);
        cfg.sipConfig.authCreds.push_back(cred);

        int id = g_next_account_id.fetch_add(1);
        auto acc = std::make_shared<MyAccount>(id);  // Use MyAccount instead of Account
        acc->create(cfg);

        g_accounts[id] = acc;

        std::cout << "[PJSUA2] Account created with ID " << id << "\n";
        return id;
    } catch (Error& err) {
        std::cerr << "[PJSUA2] Error: " << err.reason << "\n";
        return -1;
    }
}

// Create account with callbacks - now just uses the simple version since we have global callbacks
int pjsua2_create_account(const char* id_uri, const char* registrar_uri,
                         const char* username, const char* password,
                         void (*on_reg_state)(int is_registered),
                         void (*on_incoming_call)(int call_id, const char* remote_uri)) {
    // Callbacks are now global, so we ignore the per-account ones
    return pjsua2_create_account_simple(id_uri, registrar_uri, username, password);
}

// Call management
int pjsua2_make_call(int acc_id, const char* dst_uri) {
    std::lock_guard<std::mutex> lock(g_mutex);

    auto it = g_accounts.find(acc_id);
    if (it == g_accounts.end()) {
        return -1;
    }

    try {
        Call* call = new Call(*it->second);
        CallOpParam prm;
        prm.opt.audioCount = 1;
        prm.opt.videoCount = 0;

        call->makeCall(dst_uri, prm);

        int call_id = call->getId();
        g_calls[call_id] = std::shared_ptr<Call>(call);

        std::cout << "[PJSUA2] Call initiated: " << call_id << "\n";
        return call_id;
    } catch (Error& err) {
        std::cerr << "[PJSUA2] Error: " << err.reason << "\n";
        return -1;
    }
}

int pjsua2_answer_call(int call_id, int code) {
    std::lock_guard<std::mutex> lock(g_mutex);

    auto it = g_calls.find(call_id);
    if (it == g_calls.end()) {
        return -1;
    }

    try {
        CallOpParam prm;
        prm.statusCode = (pjsip_status_code)code;
        it->second->answer(prm);
        std::cout << "[PJSUA2] Call " << call_id << " answered\n";
        return 0;
    } catch (Error& err) {
        std::cerr << "[PJSUA2] Error: " << err.reason << "\n";
        return -1;
    }
}

int pjsua2_hangup_call(int call_id) {
    std::lock_guard<std::mutex> lock(g_mutex);

    auto it = g_calls.find(call_id);
    if (it == g_calls.end()) {
        return -1;
    }

    try {
        CallOpParam prm;
        it->second->hangup(prm);
        return 0;
    } catch (Error& err) {
        std::cerr << "[PJSUA2] Error: " << err.reason << "\n";
        return -1;
    }
}

// Audio devices
int pjsua2_get_audio_device_count() {
    if (!g_initialized.load() || !g_endpoint) {
        return 0;
    }

    try {
        AudDevManager& mgr = g_endpoint->audDevManager();
        const AudioDevInfoVector2 devs = mgr.enumDev2();
        return static_cast<int>(devs.size());
    } catch (...) {
        return 0;
    }
}

int pjsua2_get_audio_device_info(int index, char* name, int name_size,
                                int* is_capture, int* is_playback) {
    if (!g_initialized.load() || !g_endpoint) {
        return -1;
    }

    try {
        AudDevManager& mgr = g_endpoint->audDevManager();
        const AudioDevInfoVector2 devs = mgr.enumDev2();

        if (index < 0 || index >= (int)devs.size()) {
            return -1;
        }

        strncpy(name, devs[index].name.c_str(), name_size - 1);
        name[name_size - 1] = '\0';

        *is_capture = devs[index].inputCount > 0 ? 1 : 0;
        *is_playback = devs[index].outputCount > 0 ? 1 : 0;

        return devs[index].id;
    } catch (...) {
        return -1;
    }
}

int pjsua2_set_audio_devices(int capture_dev, int playback_dev) {
    if (!g_initialized.load() || !g_endpoint) {
        return -1;
    }

    try {
        AudDevManager& mgr = g_endpoint->audDevManager();
        mgr.setCaptureDev(capture_dev);
        mgr.setPlaybackDev(playback_dev);
        return 0;
    } catch (Error& err) {
        std::cerr << "[PJSUA2] Error: " << err.reason << "\n";
        return -1;
    }
}

// Callback setter - now properly implemented
void pjsua2_set_callbacks(void (*on_reg_state)(int acc_id, int is_registered),
                         void (*on_incoming_call)(int acc_id, int call_id, const char* remote_uri),
                         void (*on_call_state)(int call_id, int state),
                         void (*on_call_media_state)(int call_id, int state)) {
    std::cout << "[PJSUA2] Setting global callbacks\n";
    g_on_reg_state = on_reg_state;
    g_on_incoming_call = on_incoming_call;
    g_on_call_state = on_call_state;
    g_on_call_media_state = on_call_media_state;
}

// Test functions
int pjsua2_test_basic() { return 42; }
int pjsua2_test_object_creation() { return 0; }
int pjsua2_test_account_config() { return 0; }
int pjsua2_test_endpoint_state() {
    if (!g_initialized.load() || !g_endpoint) return -1;
    try { return g_endpoint->libGetState(); } catch (...) { return -1; }
}
int pjsua2_test_create_account() { return 0; }

} // extern "C"