VC++: pjproject库,IP电话简单使用示例

一、pjproject库编译

0.安装调试环境:WindowsXP + vc2008, 高版本vc打开项目文件有问题。

1.pjproject官网 http://www.pjsip.org/download.htm

2.将D:\pjproject-2.3\pjlib\include\pj\config_site_sample.h另存为config_site.h

3.安装Microsoft DirectX SDK

4.将vc的连接器->常规->附加库目录加入C:\Program Files\Microsoft DirectX SDK (February 2010)\Lib\x86

5. 附加头文件目录: 
../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include

6.附加库目录:
C:\Program Files\Microsoft DirectX SDK (February 2010)\Lib\x86

7.附加依赖项:
iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib

8.忽略特定库:
MSVCRT.LIB
 

二、简单示例

1. 头文件依赖:

#include <ddeml.h>

#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")

#include "pjsua-lib/pjsua.h"

2. WindowMain.h

#pragma once

#define TIME_CHECK  WM_USER + 1001
#define TIME_ANSWER WM_USER + 1002
#define TIMESPACE   5000

#define APPNAME     _T("SRPhone")
#define TOPICNAME   _T("Phone")
#define STRSIZE     128

typedef struct DDEDATA{
    HSZ hsz;
    int data;
    TCHAR name[STRSIZE];
    BYTE sData[STRSIZE];
}DdeData;

class CWindowMain
{
public:
    HWND m_hWnd;
    HINSTANCE m_hInstance;

    pjsua_acc_id m_acc_id;
    pjsua_call_id m_call_id;
    TCHAR m_serip[STRSIZE];
    TCHAR m_usname[STRSIZE];
    TCHAR m_psd[STRSIZE];
    int m_iCall;
    int m_iAnswer;

    DWORD m_idDde;
    HDDEDATA m_DdeReg;

    DdeData m_app;
    DdeData m_topic;
    DdeData *m_items;
    size_t m_nItemNum;

public:
    CWindowMain();
    BOOL Create();
    BOOL Show();
    LRESULT OnDestroy(UINT message, WPARAM wParam, LPARAM lParam);
    LRESULT OnCreate(UINT message, WPARAM wParam, LPARAM lParam);
    LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam);
    LRESULT OnTimer(UINT message, WPARAM wParam, LPARAM lParam);
    
    BOOL DdeInit();
    BOOL DdeDestroy();
    HDDEDATA DdeConnect(UINT wFmt, HSZ Topic, HSZ Item, HDDEDATA hData);
    HDDEDATA DdePoke(UINT wFmt, HSZ Topic, HSZ Item, HDDEDATA hData);
    HDDEDATA DdeAdvreq(UINT wFmt, HSZ Topic, HSZ Item, HDDEDATA hData);

    BOOL pjInit();
    BOOL pjDestroy();
    static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata);
    static void on_call_state(pjsua_call_id call_id, pjsip_event *e);
    static void on_call_media_state(pjsua_call_id call_id);

private:
    static HDDEDATA CALLBACK DdeCallback(UINT, UINT, HCONV, HSZ, HSZ, HDDEDATA, DWORD, DWORD);
    static CWindowMain * m_this;
    static LRESULT CALLBACK FirstWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
	static LRESULT CALLBACK DWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
};



3. WindowMain.CPP

#include "StdAfx.h"
#include "WindowMain.h"

CWindowMain * CWindowMain::m_this = NULL;

LRESULT CALLBACK CWindowMain::FirstWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
	m_this->m_hWnd = hWnd;
	SetWindowLong(hWnd, GWLP_WNDPROC, (LONG)DWndProc);
	return DefWindowProc(hWnd, message, wParam, lParam);
}

LRESULT CALLBACK CWindowMain::DWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
	m_this->WndProc(message, wParam, lParam);
	return DefWindowProc(hWnd, message, wParam, lParam);
}

BOOL CWindowMain::Create(){
    TCHAR *szWindowClass = MAKEINTATOM(FirstWndProc);

    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = FirstWndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = m_hInstance;
    wcex.hIcon = LoadIcon(m_hInstance, MAKEINTRESOURCE(32512));
    wcex.hIconSm = NULL;
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = szWindowClass;
    RegisterClassEx(&wcex);

    CreateWindow(szWindowClass, NULL, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, m_hInstance, NULL);

    return TRUE;
}

BOOL CWindowMain::Show(){
    ShowWindow(m_hWnd, SW_SHOW);
    UpdateWindow(m_hWnd);
    return TRUE;
}

CWindowMain::CWindowMain(){
    m_this = this;
    m_hInstance = GetModuleHandle(NULL);
}

LRESULT CWindowMain::OnCreate(UINT message, WPARAM wParam, LPARAM lParam){
    if (!DdeInit()){
        PostQuitMessage(0);
    }

    memset(m_serip, 0, STRSIZE);
    memset(m_usname, 0, STRSIZE);
    memset(m_psd, 0, STRSIZE);
    GetPrivateProfileString(_T("TCP/IP"), _T("serverip"), NULL, m_serip, STRSIZE, _T(".//cfg.ini"));
    GetPrivateProfileString(_T("TCP/IP"), _T("username"), NULL, m_usname, STRSIZE, _T(".//cfg.ini"));
    GetPrivateProfileString(_T("TCP/IP"), _T("psd"), NULL, m_psd, STRSIZE, _T(".//cfg.ini"));

    if(m_serip[0] == 0 || m_usname == 0 || m_psd[0] == 0){
        PostQuitMessage(0);
    }

    if (!pjInit()){
        PostQuitMessage(0);
    }
    return TRUE;
}

LRESULT CWindowMain::OnDestroy(UINT message, WPARAM wParam, LPARAM lParam){
    DdeDestroy();
    PostQuitMessage(0);
    return TRUE;
}

LRESULT CWindowMain::WndProc(UINT message, WPARAM wParam, LPARAM lParam){
    switch (message){
    case WM_CREATE:
        OnCreate(message, wParam, lParam);
        break;
    case WM_TIMER:
        OnTimer(message, wParam, lParam);
        break;
    case WM_DESTROY:
        OnDestroy(message, wParam, lParam);
        break;
    }
    return 0;
}

BOOL CWindowMain::DdeInit(){
    //DDE ID
    m_idDde = NULL;
    m_DdeReg = NULL;
    m_items = NULL;
    m_nItemNum = 0;

    //m_app初始化
    m_app.hsz = NULL;
    m_app.data = 0;
    memset(m_app.sData, 0, STRSIZE);
    _tcsncpy_s((TCHAR *)m_app.name, STRSIZE, APPNAME, STRSIZE - 1);

    //m_topic初始化
    m_topic.hsz = NULL;
    m_topic.data = 0;
    memset(m_topic.sData, 0, STRSIZE);
    _tcsncpy_s((TCHAR *)m_topic.name, STRSIZE, TOPICNAME, STRSIZE - 1);

    //m_items初始化
    TCHAR *sitems[] = {
        _T("FreeSwitchStatus"),
        _T("SRPhoneStatus"),
        _T("SIPHardWareStatus"),
        _T("CallNumber"),
        _T("Call"),
        _T("AnswerNumber"),
        _T("Answer"),
        _T("End"),
    };
    m_nItemNum = sizeof(sitems)/sizeof(TCHAR *);
    m_items = new DdeData[m_nItemNum];
    for (size_t i = 0; i < m_nItemNum; i++){
        m_items[i].hsz = NULL;
        m_items[i].data = 0;
        memset(m_items[i].sData, 0, STRSIZE);
        _tcsncpy_s((TCHAR *)m_items[i].name, STRSIZE, sitems[i], STRSIZE - 1);
    }

    //|CBF_SKIP_CONNECT_CONFIRMS|CBF_FAIL_SELFCONNECTIONS|CBF_FAIL_POKES|CBF_FAIL_EXECUTES
    UINT uDdeInit = DdeInitialize(&m_idDde, (PFNCALLBACK)DdeCallback, APPCMD_FILTERINITS, 0);
    if(uDdeInit != DMLERR_NO_ERROR){
        //如果函数执行成功则会返回一个0值,DMLERR_NO_ERROR
        //如果错误则会返回非零值,用以标示它的出错类型
        //DDE SERVER初始化失败
        return FALSE;
    }

    m_app.hsz = DdeCreateStringHandle(m_idDde, m_app.name, 0);
    if(m_app.hsz == 0){
        return FALSE;
    }

    m_topic.hsz = DdeCreateStringHandle(m_idDde, m_topic.name, 0);
    if(m_topic.hsz == 0){
        return FALSE;
    }

    for (size_t i = 0; i < m_nItemNum; i++){
        m_items[i].hsz = DdeCreateStringHandle(m_idDde, m_items[i].name, 0);
        if(m_items[i].hsz == 0){
            return FALSE;
        }
    }

    //注册服务
    m_DdeReg = DdeNameService(m_idDde, m_app.hsz, NULL, DNS_REGISTER);
    if(m_DdeReg == 0){
        return FALSE;
    }

    return TRUE;
}

BOOL CWindowMain::DdeDestroy(){
    if (m_DdeReg != 0){
        DdeNameService(m_idDde, 0, 0, DNS_UNREGISTER);
    }

    if (m_app.hsz != NULL){
        DdeFreeStringHandle(m_idDde, m_app.hsz);
    }

    if (m_topic.hsz != NULL){
        DdeFreeStringHandle(m_idDde, m_topic.hsz);
    }
    
    for (size_t i = 0; i < m_nItemNum; i++){
        if (m_items[i].hsz != NULL){
            DdeFreeStringHandle(m_idDde, m_items[i].hsz);
        }
    }

    if (m_idDde != NULL){
        DdeUninitialize(m_idDde);
    }

    if (m_items != NULL)
    {
        delete m_items;
    }

    return TRUE;
}

HDDEDATA CWindowMain::DdeCallback(UINT wType, UINT wFmt, HCONV hConv, HSZ Topic, 
    HSZ Item, HDDEDATA hData, DWORD lData1, DWORD lData2)
{
    switch(wType){
     case XTYP_ADVSTART:        //通知服务器,一个客户端正在就(hsz1和hsz2表示话题和条目)发起一个报告循环,使报告循环有效
         return (HDDEDATA)TRUE;
     case XTYP_ERROR:           //DDE已出现严重错误
         return (HDDEDATA)FALSE;
    case XTYP_CONNECT:          //XTYP_CONNECT响应于客户程序使用的DdeConnect()函数
        return m_this->DdeConnect(wFmt, Topic, Item, hData);
    case XTYP_POKE:             //客户端向服务器发送数据,服务器接收数据的地方
        return m_this->DdePoke(wFmt, Topic, Item, hData);
     case XTYP_ADVREQ:          //服务器主动向客户端发送数据
         return m_this->DdeAdvreq(wFmt, Topic, Item, hData);
    case XTYP_REQUEST:
        /*
        XTYP_REQUEST响应客户程序中使用DdeClientTransaction()函数的XTYP_REQUEST和选项
        XTYP_EXECUTE响应客户程序中使用DdeClientTransaction()函数的XTYP_EXECUTE和选项
        在服务程序中,对于XTYP_REQUEST选项,可以用DdeCreateDataHandle函数向客户程序发送数据,而XTYP_EXECUTE则不能
        XTYP_EXECUTE选项,可以用DdeGetData()函数从客户获取数据,而XTYP_REQUEST则不能。
        如果返回NULL,客户端接收到	DDE_FNOTPROCESSED
        */
        return (HDDEDATA)TRUE;
    }

    return (HDDEDATA)FALSE;
}

HDDEDATA CWindowMain::DdeConnect(UINT wFmt, HSZ topic, HSZ item, HDDEDATA hData){
    TCHAR serverName[STRSIZE];
    TCHAR topicName[STRSIZE];

#ifdef UNICODE
    //获得服务名称字符串
    DdeQueryString(m_idDde, item, serverName, STRSIZE - 1, CP_WINUNICODE);
    //获得话题名称字符串
    DdeQueryString(m_idDde, topic, topicName, STRSIZE - 1, CP_WINUNICODE);
#else
    DdeQueryString(m_idDde, item, serverName, STRSIZE - 1, CP_WINANSI);
    DdeQueryString(m_idDde, topic, topicName, STRSIZE - 1, CP_WINANSI);
#endif

    if(_tcscmp(serverName, m_app.name) != 0){
        return (HDDEDATA)FALSE;
    }

    if(_tcscmp(topicName, m_topic.name) != 0){
        return (HDDEDATA)FALSE;
    }

    //验证通过,允许连接
    return (HDDEDATA)TRUE;
}

//客户端向服务器发送数据,服务器接收数据的地方
HDDEDATA CWindowMain::DdePoke(UINT wFmt, HSZ topic, HSZ item, HDDEDATA hData){
    size_t i = 3;
    for (i = 3; i < m_nItemNum; i++){
        if(item == m_items[i].hsz){
            break;
        }
    }

    if (i == 8)
    {
        return (HDDEDATA)DDE_FACK;
    }

    //取得数据
    DdeGetData(hData, m_items[i].sData, STRSIZE - 1, 0);

    if(_tcscmp(m_items[i].name, _T("Call")) == 0){
        int ai = atoi((char *)m_items[i].sData);
        if (ai == 1)
        {
            //Call CallNumber m_items[3].sData;
            if (pjsua_call_get_count() > 0)
            {
                return (HDDEDATA)DDE_FACK;
            }
            char url[STRSIZE] = {0};
            sprintf(url, "sip:%s@%s", m_items[3].sData, m_serip);

            pj_status_t status;
            status = pjsua_verify_url(url);
            if (status != PJ_SUCCESS){
                return (HDDEDATA)DDE_FACK;
            }
            pj_str_t uri = pj_str(url);
            status = pjsua_call_make_call(m_acc_id, &uri, 0, NULL, NULL, NULL);
            if (status != PJ_SUCCESS){
                return (HDDEDATA)DDE_FACK;
            }
            SetTimer(m_hWnd, TIME_ANSWER, 1000, NULL);
        }
        
        return (HDDEDATA)DDE_FACK;
    }

    if(_tcscmp(m_items[i].name, _T("Answer")) == 0){
        int ai = atoi((char *)m_items[i].sData);
        if (ai == 1)
        {
            //Answer AnswerNumber m_items[5].sData;
            KillTimer(m_hWnd, TIME_ANSWER);
            pjsua_call_hangup_all();

            pjsua_call_answer(m_call_id, 200, NULL, NULL);
        }
        return (HDDEDATA)DDE_FACK;
    }

    if(_tcscmp(m_items[i].name, _T("End")) == 0){
        int ai = atoi((char *)m_items[i].sData);
        if (ai == 1)
        {
            //End
            KillTimer(m_hWnd, TIME_ANSWER);
            pjsua_call_hangup_all();
        }
        
        return (HDDEDATA)DDE_FACK;
    }

    return (HDDEDATA)DDE_FACK;
}

//服务器主动向客户端发送数据
LRESULT CWindowMain::OnTimer(UINT message, WPARAM wParam, LPARAM lParam){
    switch (wParam){
    case TIME_ANSWER:
        if (m_iCall++ >= 60)
        {
            KillTimer(m_hWnd, TIME_ANSWER);
            pjsua_call_hangup_all();
            break;
        }
        sndPlaySound(_T("ringin.wav"), SND_ASYNC);
        break;
    case TIME_CHECK:
        {
            pjsua_acc_info info;
            pjsua_acc_get_info(m_acc_id, &info);

            //FreeSwitch状态
            //在线
            if(info.status == PJSIP_SC_OK)
            {
                strncpy_s((char *)m_items[0].sData, STRSIZE, _T("1"), STRSIZE - 1);
            }
            //离线
            if (info.status == PJSIP_SC_FORBIDDEN)
            {
                strncpy_s((char *)m_items[0].sData, STRSIZE, _T("0"), STRSIZE - 1);
            }
            DdePostAdvise(m_idDde, m_topic.hsz, m_items[0].hsz);

            //软电话状态
            //在线
            //离线


            //ip电话状态
            //在线
            //离线


        }
        break;
    default:
        break;
    }

    return TRUE;
}
//服务器主动向客户端发送数据
HDDEDATA CWindowMain::DdeAdvreq(UINT wFmt, HSZ topic, HSZ item, HDDEDATA hData){
    TCHAR data[STRSIZE];

#ifdef UNICODE
    DdeQueryString(m_idDde, item, data, STRSIZE - 1, CP_WINUNICODE);
#else
    DdeQueryString(m_idDde, item, data, STRSIZE - 1, CP_WINANSI); 
#endif

    for (size_t i = 0; i < m_nItemNum; i++){
        if(_tcscmp(data, m_items[i].name) == 0){
            HDDEDATA ret = DdeCreateDataHandle(m_idDde, m_items[i].sData, STRSIZE, 0, item, wFmt, NULL);
            return ret;
        }
    }

    //条目验证失败
    return (HDDEDATA)NULL;
}

BOOL CWindowMain::pjInit(){
    pj_status_t status;
    status = pjsua_create();
    if (status != PJ_SUCCESS) 
        return FALSE;

    pjsua_config cfg;
    pjsua_logging_config log_cfg;

    pjsua_config_default(&cfg);
    cfg.cb.on_incoming_call = &on_incoming_call;
    cfg.cb.on_call_media_state = &on_call_media_state;
    cfg.cb.on_call_state = &on_call_state;

    pjsua_logging_config_default(&log_cfg);
    log_cfg.console_level = 4;

    status = pjsua_init(&cfg, &log_cfg, NULL);
    if (status != PJ_SUCCESS) 
        return FALSE;

    pjsua_transport_config cfg1;
    pjsua_transport_config_default(&cfg1);
    cfg1.port = 5061;
    status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &cfg1, NULL);
    if (status != PJ_SUCCESS) 
        return FALSE;

    status = pjsua_start();
    if (status != PJ_SUCCESS) 
        return FALSE;

    pjsua_acc_config cfg2;
    pjsua_acc_config_default(&cfg2);

    char ss[STRSIZE]={0};
    sprintf(ss, "sip:%s@%s", m_usname, m_serip);
    cfg2.id = pj_str(ss);

    char ss2[STRSIZE]={0};
    sprintf(ss2, "sip:%s", m_serip);
    cfg2.reg_uri = pj_str(ss2);

    cfg2.cred_count = 1;
    cfg2.cred_info[0].realm = pj_str(m_serip);
    cfg2.cred_info[0].scheme = pj_str("digest");
    cfg2.cred_info[0].username = pj_str(m_usname);
    cfg2.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
    cfg2.cred_info[0].data = pj_str(m_psd);

    status = pjsua_acc_add(&cfg2, PJ_TRUE, &m_acc_id);
    if (status != PJ_SUCCESS)
        return FALSE;

    SetTimer(m_hWnd, TIME_CHECK, TIMESPACE, NULL);

    return TRUE;
}

BOOL CWindowMain::pjDestroy(){
    pjsua_destroy();
    KillTimer(m_hWnd, TIME_CHECK);
    return TRUE;
}

void CWindowMain::on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata)
{
    pjsua_call_info ci;

    PJ_UNUSED_ARG(acc_id);
    PJ_UNUSED_ARG(rdata);

    pjsua_call_get_info(call_id, &ci);


    char str[STRSIZE] = {0};
    strcpy(str, ci.remote_info.ptr);
    char * pa1 = strchr(str, ':');
    char * pa2 = strchr(str, '@');
    *pa2 = NULL;

    m_this->m_call_id = call_id;

    SetTimer(m_this->m_hWnd, TIME_ANSWER, 1000, NULL);
    m_this->m_iCall = 0;

}

void CWindowMain::on_call_state(pjsua_call_id call_id, pjsip_event *e)
{
    pjsua_call_info ci;

    PJ_UNUSED_ARG(e);

    pjsua_call_get_info(call_id, &ci);

    switch(ci.state){
        case PJSIP_INV_STATE_DISCONNECTED:
            KillTimer(m_this->m_hWnd, TIME_ANSWER);
            pjsua_call_hangup_all();
            break;
        default:
            break;
    }
}

void CWindowMain::on_call_media_state(pjsua_call_id call_id)
{
    pjsua_call_info ci;

    pjsua_call_get_info(call_id, &ci);

    if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
        pjsua_conf_connect(ci.conf_slot, 0);
        pjsua_conf_connect(0, ci.conf_slot);
    }
}

3. _tWinMain 程序入口函数

int WINAPI _tWinMain(HINSTANCE hInstance1, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow){
    CWindowMain wnd;
    wnd.Create();
    wnd.Show();

    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int) msg.wParam;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值