1
// pjsua_wince.cpp : Defines the entry point for the application.
5
#include "pjsua_wince.h"
7
#include <pjsua-lib/pjsua.h>
9
#define MAX_LOADSTRING 100
12
static HINSTANCE hInst;
15
static HWND hwndGlobalStatus, hwndURI, hwndCallStatus;
16
static HWND hwndActionButton, hwndExitButton;
27
// Destination URI (to make call, or to subscribe presence)
29
#define SIP_DST_URI "sip:192.168.0.7:5061"
34
#define HAS_SIP_ACCOUNT 0 // 0 to disable registration
35
#define SIP_DOMAIN "server"
36
#define SIP_REALM "server"
37
#define SIP_USER "user"
38
#define SIP_PASSWD "secret"
41
// Outbound proxy for all accounts
43
#define SIP_PROXY NULL
44
//#define SIP_PROXY "sip:192.168.0.2;lr"
48
// Configure nameserver if DNS SRV is to be used with both SIP
49
// or STUN (for STUN see other settings below)
51
#define NAMESERVER NULL
52
//#define NAMESERVER "62.241.163.201"
57
// Use this to have the STUN server resolved normally
58
# define STUN_DOMAIN NULL
59
# define STUN_SERVER "stun.fwdnet.net"
61
// Use this to have the STUN server resolved with DNS SRV
62
# define STUN_DOMAIN "iptel.org"
63
# define STUN_SERVER NULL
65
// Use this to disable STUN
66
# define STUN_DOMAIN NULL
67
# define STUN_SERVER NULL
79
static pj_pool_t *g_pool;
80
static pj_str_t g_local_uri;
81
static int g_current_acc;
82
static int g_current_call = PJSUA_INVALID_ID;
83
static int g_current_action;
87
ID_GLOBAL_STATUS = 21,
103
// Forward declarations of functions included in this code module:
104
static ATOM MyRegisterClass (HINSTANCE, LPTSTR);
105
BOOL InitInstance (HINSTANCE, int);
106
static void OnCreate (HWND hWnd);
107
static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
111
/////////////////////////////////////////////////////////////////////////////
113
static void OnError(const wchar_t *title, pj_status_t status)
115
char errmsg[PJ_ERR_MSG_SIZE];
116
PJ_DECL_UNICODE_TEMP_BUF(werrmsg, PJ_ERR_MSG_SIZE);
118
pj_strerror(status, errmsg, sizeof(errmsg));
120
MessageBox(NULL, PJ_STRING_TO_NATIVE(errmsg, werrmsg, PJ_ERR_MSG_SIZE),
125
static void SetLocalURI(const char *uri, int len, bool enabled=true)
128
if (len==-1) len=pj_ansi_strlen(uri);
129
pj_ansi_to_unicode(uri, len, tmp, PJ_ARRAY_SIZE(tmp));
130
SetDlgItemText(hMainWnd, ID_GLOBAL_STATUS, tmp);
131
EnableWindow(hwndGlobalStatus, enabled?TRUE:FALSE);
136
static void SetURI(const char *uri, int len, bool enabled=true)
139
if (len==-1) len=pj_ansi_strlen(uri);
140
pj_ansi_to_unicode(uri, len, tmp, PJ_ARRAY_SIZE(tmp));
141
SetDlgItemText(hMainWnd, ID_URI, tmp);
142
EnableWindow(hwndURI, enabled?TRUE:FALSE);
146
static void SetCallStatus(const char *state, int len)
149
if (len==-1) len=pj_ansi_strlen(state);
150
pj_ansi_to_unicode(state, len, tmp, PJ_ARRAY_SIZE(tmp));
151
SetDlgItemText(hMainWnd, ID_CALL_STATUS, tmp);
154
static void SetAction(int action, bool enable=true)
158
hMenu = CommandBar_GetMenu(hwndCB, 0);
160
RemoveMenu(hMenu, ID_MENU_NONE, MF_BYCOMMAND);
161
RemoveMenu(hMenu, ID_MENU_CALL, MF_BYCOMMAND);
162
RemoveMenu(hMenu, ID_MENU_ANSWER, MF_BYCOMMAND);
163
RemoveMenu(hMenu, ID_MENU_DISCONNECT, MF_BYCOMMAND);
167
InsertMenu(hMenu, ID_EXIT, MF_BYCOMMAND, action, TEXT("None"));
168
SetWindowText(hwndActionButton, TEXT("-"));
171
InsertMenu(hMenu, ID_EXIT, MF_BYCOMMAND, action, TEXT("Call"));
172
SetWindowText(hwndActionButton, TEXT("&Call"));
175
InsertMenu(hMenu, ID_EXIT, MF_BYCOMMAND, action, TEXT("Answer"));
176
SetWindowText(hwndActionButton, TEXT("&Answer"));
178
case ID_MENU_DISCONNECT:
179
InsertMenu(hMenu, ID_EXIT, MF_BYCOMMAND, action, TEXT("Hangup"));
180
SetWindowText(hwndActionButton, TEXT("&Hangup"));
184
EnableMenuItem(hMenu, action, MF_BYCOMMAND | (enable?MF_ENABLED:MF_GRAYED));
185
DrawMenuBar(hMainWnd);
187
g_current_action = action;
192
* Handler when invite state has changed.
194
static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
196
pjsua_call_info call_info;
200
pjsua_call_get_info(call_id, &call_info);
202
if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) {
204
g_current_call = PJSUA_INVALID_ID;
205
SetURI(SIP_DST_URI, -1);
206
SetAction(ID_MENU_CALL);
207
//SetCallStatus(call_info.state_text.ptr, call_info.state_text.slen);
208
SetCallStatus(call_info.last_status_text.ptr, call_info.last_status_text.slen);
211
//if (g_current_call == PJSUA_INVALID_ID)
212
// g_current_call = call_id;
214
if (call_info.remote_contact.slen)
215
SetURI(call_info.remote_contact.ptr, call_info.remote_contact.slen, false);
217
SetURI(call_info.remote_info.ptr, call_info.remote_info.slen, false);
219
if (call_info.state == PJSIP_INV_STATE_CONFIRMED)
220
SetAction(ID_MENU_DISCONNECT);
222
SetCallStatus(call_info.state_text.ptr, call_info.state_text.slen);
228
* Callback on media state changed event.
229
* The action may connect the call to sound device, to file, or
232
static void on_call_media_state(pjsua_call_id call_id)
234
pjsua_call_info call_info;
236
pjsua_call_get_info(call_id, &call_info);
238
if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
239
pjsua_conf_connect(call_info.conf_slot, 0);
240
pjsua_conf_connect(0, call_info.conf_slot);
246
* Handler when there is incoming call.
248
static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
249
pjsip_rx_data *rdata)
251
pjsua_call_info call_info;
253
PJ_UNUSED_ARG(acc_id);
254
PJ_UNUSED_ARG(rdata);
256
if (g_current_call != PJSUA_INVALID_ID) {
258
reason = pj_str("Another call is in progress");
259
pjsua_call_answer(call_id, PJSIP_SC_BUSY_HERE, &reason, NULL);
263
g_current_call = call_id;
265
pjsua_call_get_info(call_id, &call_info);
267
SetAction(ID_MENU_ANSWER);
268
SetURI(call_info.remote_info.ptr, call_info.remote_info.slen, false);
269
pjsua_call_answer(call_id, 200, NULL, NULL);
274
* Handler registration status has changed.
276
static void on_reg_state(pjsua_acc_id acc_id)
278
PJ_UNUSED_ARG(acc_id);
280
// Log already written.
285
* Handler on buddy state changed.
287
static void on_buddy_state(pjsua_buddy_id buddy_id)
289
/* Currently this is not processed */
290
PJ_UNUSED_ARG(buddy_id);
295
* Incoming IM message (i.e. MESSAGE request)!
297
static void on_pager(pjsua_call_id call_id, const pj_str_t *from,
298
const pj_str_t *to, const pj_str_t *contact,
299
const pj_str_t *mime_type, const pj_str_t *text)
301
/* Currently this is not processed */
302
PJ_UNUSED_ARG(call_id);
305
PJ_UNUSED_ARG(contact);
306
PJ_UNUSED_ARG(mime_type);
312
* Received typing indication
314
static void on_typing(pjsua_call_id call_id, const pj_str_t *from,
315
const pj_str_t *to, const pj_str_t *contact,
318
/* Currently this is not processed */
319
PJ_UNUSED_ARG(call_id);
322
PJ_UNUSED_ARG(contact);
323
PJ_UNUSED_ARG(is_typing);
327
* Callback upon NAT detection completion
329
static void nat_detect_cb(const pj_stun_nat_detect_result *res)
331
if (res->status != PJ_SUCCESS) {
333
pj_ansi_snprintf(msg, sizeof(msg), "NAT detection failed: %s",
335
SetCallStatus(msg, pj_ansi_strlen(msg));
338
pj_ansi_snprintf(msg, sizeof(msg), "NAT type is %s",
340
SetCallStatus(msg, pj_ansi_strlen(msg));
345
static BOOL OnInitStack(void)
348
pjsua_logging_config log_cfg;
349
pjsua_media_config media_cfg;
350
pjsua_transport_config udp_cfg;
351
pjsua_transport_config rtp_cfg;
352
pjsua_transport_id transport_id;
353
pjsua_transport_info transport_info;
358
status = pjsua_create();
359
if (status != PJ_SUCCESS) {
360
OnError(TEXT("Error creating pjsua"), status);
364
/* Create global pool for application */
365
g_pool = pjsua_pool_create("pjsua", 4000, 4000);
368
pjsua_config_default(&cfg);
369
pjsua_media_config_default(&media_cfg);
370
pjsua_transport_config_default(&udp_cfg);
371
udp_cfg.port = SIP_PORT;
373
pjsua_transport_config_default(&rtp_cfg);
374
rtp_cfg.port = 40000;
376
pjsua_logging_config_default(&log_cfg);
378
log_cfg.log_filename = pj_str("\\pjsua.txt");
379
log_cfg.msg_logging = 1;
380
log_cfg.decor = pj_log_get_decor() | PJ_LOG_HAS_CR;
383
media_cfg.clock_rate = 8000;
384
media_cfg.ec_options = PJMEDIA_ECHO_SIMPLE;
385
media_cfg.ec_tail_len = 256;
386
// use default quality setting
387
//media_cfg.quality = 1;
388
media_cfg.ptime = 20;
389
media_cfg.enable_ice = USE_ICE;
391
/* Initialize application callbacks */
392
cfg.cb.on_call_state = &on_call_state;
393
cfg.cb.on_call_media_state = &on_call_media_state;
394
cfg.cb.on_incoming_call = &on_incoming_call;
395
cfg.cb.on_reg_state = &on_reg_state;
396
cfg.cb.on_buddy_state = &on_buddy_state;
397
cfg.cb.on_pager = &on_pager;
398
cfg.cb.on_typing = &on_typing;
399
cfg.cb.on_nat_detect = &nat_detect_cb;
402
cfg.outbound_proxy_cnt = 1;
403
cfg.outbound_proxy[0] = pj_str(SIP_PROXY);
407
cfg.nameserver_count = 1;
408
cfg.nameserver[0] = pj_str(NAMESERVER);
411
if (NAMESERVER && STUN_DOMAIN) {
412
cfg.stun_domain = pj_str(STUN_DOMAIN);
413
} else if (STUN_SERVER) {
414
cfg.stun_host = pj_str(STUN_SERVER);
418
/* Initialize pjsua */
419
status = pjsua_init(&cfg, &log_cfg, &media_cfg);
420
if (status != PJ_SUCCESS) {
421
OnError(TEXT("Initialization error"), status);
425
/* Set codec priority */
426
pjsua_codec_set_priority(pj_cstr(&tmp, "pcmu"), 240);
427
pjsua_codec_set_priority(pj_cstr(&tmp, "pcma"), 230);
428
pjsua_codec_set_priority(pj_cstr(&tmp, "speex/8000"), 190);
429
pjsua_codec_set_priority(pj_cstr(&tmp, "ilbc"), 189);
430
pjsua_codec_set_priority(pj_cstr(&tmp, "speex/16000"), 180);
431
pjsua_codec_set_priority(pj_cstr(&tmp, "speex/32000"), 0);
432
pjsua_codec_set_priority(pj_cstr(&tmp, "gsm"), 100);
435
/* Add UDP transport and the corresponding PJSUA account */
436
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP,
437
&udp_cfg, &transport_id);
438
if (status != PJ_SUCCESS) {
439
OnError(TEXT("Error starting SIP transport"), status);
443
pjsua_transport_get_info(transport_id, &transport_info);
445
g_local_uri.ptr = (char*)pj_pool_alloc(g_pool, 128);
446
g_local_uri.slen = pj_ansi_sprintf(g_local_uri.ptr,
448
(int)transport_info.local_name.host.slen,
449
transport_info.local_name.host.ptr,
450
transport_info.local_name.port);
453
/* Add local account */
454
pjsua_acc_add_local(transport_id, PJ_TRUE, &g_current_acc);
455
pjsua_acc_set_online_status(g_current_acc, PJ_TRUE);
458
if (HAS_SIP_ACCOUNT) {
459
pjsua_acc_config cfg;
461
pjsua_acc_config_default(&cfg);
462
cfg.id = pj_str("sip:" SIP_USER "@" SIP_DOMAIN);
463
cfg.reg_uri = pj_str("sip:" SIP_DOMAIN);
465
cfg.cred_info[0].realm = pj_str(SIP_REALM);
466
cfg.cred_info[0].scheme = pj_str("digest");
467
cfg.cred_info[0].username = pj_str(SIP_USER);
468
cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
469
cfg.cred_info[0].data = pj_str(SIP_PASSWD);
471
status = pjsua_acc_add(&cfg, PJ_TRUE, &g_current_acc);
472
if (status != PJ_SUCCESS) {
480
pjsua_buddy_config bcfg;
482
pjsua_buddy_config_default(&bcfg);
483
bcfg.uri = pj_str(SIP_DST_URI);
484
bcfg.subscribe = PJ_FALSE;
486
pjsua_buddy_add(&bcfg, NULL);
490
status = pjsua_start();
491
if (status != PJ_SUCCESS) {
492
OnError(TEXT("Error starting pjsua"), status);
500
//////////////////////////////////////////////////////////////////////////////
502
int WINAPI WinMain(HINSTANCE hInstance,
503
HINSTANCE hPrevInstance,
510
PJ_UNUSED_ARG(lpCmdLine);
511
PJ_UNUSED_ARG(hPrevInstance);
513
// Perform application initialization:
514
if (!InitInstance (hInstance, nCmdShow))
519
hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_PJSUA_WINCE);
522
// Main message loop:
523
while (GetMessage(&msg, NULL, 0, 0))
525
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
527
TranslateMessage(&msg);
528
DispatchMessage(&msg);
535
static ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass)
539
wc.style = CS_HREDRAW | CS_VREDRAW;
540
wc.lpfnWndProc = (WNDPROC) WndProc;
543
wc.hInstance = hInstance;
544
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PJSUA_WINCE));
546
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
548
wc.lpszClassName = szWindowClass;
550
return RegisterClass(&wc);
554
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
557
TCHAR szTitle[MAX_LOADSTRING];
558
TCHAR szWindowClass[MAX_LOADSTRING];
563
if (OnInitStack() == FALSE)
566
LoadString(hInstance, IDC_PJSUA_WINCE, szWindowClass, MAX_LOADSTRING);
567
MyRegisterClass(hInstance, szWindowClass);
569
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
570
hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE,
571
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 200,
572
NULL, NULL, hInstance, NULL);
580
ShowWindow(hWnd, nCmdShow);
583
CommandBar_Show(hwndCB, TRUE);
585
SetTimer(hMainWnd, ID_POLL_TIMER, 50, NULL);
587
pjsua_detect_nat_type();
592
static void OnCreate(HWND hWnd)
606
hwndCB = CommandBar_Create(hInst, hWnd, 1);
607
CommandBar_InsertMenubar(hwndCB, hInst, IDM_MENU, 0);
608
CommandBar_AddAdornments(hwndCB, 0, 0);
610
// Create global status text
611
dwStyle = WS_CHILD | WS_VISIBLE | WS_DISABLED | ES_LEFT;
612
hwndGlobalStatus = CreateWindow(
613
TEXT("EDIT"), // Class name
615
dwStyle, // Window style
616
X, // x-coordinate of the upper-left corner
617
Y+0, // y-coordinate of the upper-left corner
618
W, // Width of the window for the edit
620
H-5, // Height of the window for the edit
622
hWnd, // Window handle to the parent window
623
(HMENU) ID_GLOBAL_STATUS, // Control identifier
624
hInst, // Instance handle
625
NULL); // Specify NULL for this parameter when
626
// you create a control
627
SetLocalURI(g_local_uri.ptr, g_local_uri.slen, false);
631
dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER;
632
hwndURI = CreateWindow (
633
TEXT("EDIT"), // Class name
635
dwStyle, // Window style
636
X, // x-coordinate of the upper-left corner
637
Y+H, // y-coordinate of the upper-left corner
638
W, // Width of the window for the edit
640
H-5, // Height of the window for the edit
642
hWnd, // Window handle to the parent window
643
(HMENU) ID_URI, // Control identifier
644
hInst, // Instance handle
645
NULL); // Specify NULL for this parameter when
646
// you create a control
648
// Create action Button
649
hwndActionButton = CreateWindow( L"button", L"Action",
650
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
653
(HMENU) ID_BTN_ACTION,
656
// Create exit button
657
hwndExitButton = CreateWindow( L"button", L"E&xit",
658
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
665
// Create call status edit
666
dwStyle = WS_CHILD | WS_VISIBLE | WS_DISABLED;
667
hwndCallStatus = CreateWindow (
668
TEXT("EDIT"), // Class name
670
dwStyle, // Window style
671
X, // x-coordinate of the upper-left corner
672
Y+3*H, // y-coordinate of the upper-left corner
673
W, // Width of the window for the edit
675
H-5, // Height of the window for the edit
677
hWnd, // Window handle to the parent window
678
(HMENU) ID_CALL_STATUS, // Control identifier
679
hInst, // Instance handle
680
NULL); // Specify NULL for this parameter when
681
// you create a control
682
SetCallStatus("Ready", 5);
683
SetAction(ID_MENU_CALL);
684
SetURI(SIP_DST_URI, -1);
690
static void OnDestroy(void)
695
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
702
wParam = ID_MENU_CALL;
703
} else if (wParam==115) {
704
if (g_current_call == PJSUA_INVALID_ID)
707
wParam = ID_MENU_DISCONNECT;
712
wmId = LOWORD(wParam);
713
wmEvent = HIWORD(wParam);
714
if (wmId == ID_BTN_ACTION)
715
wmId = g_current_action;
719
if (g_current_call != PJSUA_INVALID_ID) {
720
MessageBox(NULL, TEXT("Can not make call"),
721
TEXT("You already have one call active"), MB_OK);
728
GetWindowText(hwndURI, text, PJ_ARRAY_SIZE(text));
729
pj_unicode_to_ansi(text, pj_unicode_strlen(text),
732
dst_uri.slen = pj_ansi_strlen(tmp);
733
status = pjsua_call_make_call(g_current_acc,
735
NULL, &g_current_call);
736
if (status != PJ_SUCCESS)
737
OnError(TEXT("Unable to make call"), status);
740
if (g_current_call == PJSUA_INVALID_ID)
741
MessageBox(NULL, TEXT("Can not answer"),
742
TEXT("There is no call!"), MB_OK);
744
pjsua_call_answer(g_current_call, 200, NULL, NULL);
746
case ID_MENU_DISCONNECT:
747
if (g_current_call == PJSUA_INVALID_ID)
748
MessageBox(NULL, TEXT("Can not disconnect"),
749
TEXT("There is no call!"), MB_OK);
751
pjsua_call_hangup(g_current_call, PJSIP_SC_DECLINE, NULL, NULL);
757
return DefWindowProc(hWnd, message, wParam, lParam);
767
CommandBar_Destroy(hwndCB);
772
pjsua_handle_events(1);
776
return DefWindowProc(hWnd, message, wParam, lParam);