- Timestamp:
- Jun 13, 2006 10:57:13 PM (18 years ago)
- Location:
- pjproject/trunk
- Files:
-
- 4 added
- 5 deleted
- 12 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjlib/src/pj/config.c
r468 r503 22 22 23 23 static const char *id = "config.c"; 24 const char *PJ_VERSION = "0.5. 5.6";24 const char *PJ_VERSION = "0.5.6.0"; 25 25 26 26 PJ_DEF(void) pj_dump_config(void) -
pjproject/trunk/pjsip-apps/build
- Property svn:ignore
-
old new 8 8 *.suo 9 9 *.pdb 10 activex-pjsua_p.c 11 dlldata.c
-
- Property svn:ignore
-
pjproject/trunk/pjsip-apps/build/activex-pjsua.dsp
r487 r503 324 324 # Begin Source File 325 325 326 SOURCE="..\src\activex-pjsua\pjsua .h"326 SOURCE="..\src\activex-pjsua\pjsua-structs.h" 327 327 # End Source File 328 328 # Begin Source File -
pjproject/trunk/pjsip-apps/build/pjsua.dsp
r458 r503 93 93 SOURCE=..\src\pjsua\main.c 94 94 # End Source File 95 # Begin Source File 96 97 SOURCE=..\src\pjsua\pjsua.c 98 # End Source File 95 99 # End Group 96 100 # Begin Group "Header Files" -
pjproject/trunk/pjsip-apps/src/activex-pjsua
-
Property
svn:ignore
set to
old*
backup*
*.tlb
*.aps
activex-pjsua.h
activex-pjsua_i.c
pjsua-structs.h
resource.h
-
Property
svn:ignore
set to
-
pjproject/trunk/pjsip-apps/src/pjsua/main.c
r492 r503 18 18 */ 19 19 #include <pjsua-lib/pjsua.h> 20 #include <pjsua-lib/pjsua_console_app.h>21 22 20 23 21 #define THIS_FILE "main.c" 24 22 25 /***************************************************************************** 26 * main(): 23 24 /* 25 * These are defined in pjsua.c. 27 26 */ 27 pj_status_t app_init(int argc, char *argv[]); 28 pj_status_t app_main(void); 29 pj_status_t app_destroy(void); 30 28 31 int main(int argc, char *argv[]) 29 32 { 30 pjsua_config cfg; 31 pj_str_t uri_to_call = { NULL, 0 }; 32 33 /* Init default settings. */ 34 pjsua_default_config(&cfg); 35 36 37 /* Create PJLIB and memory pool */ 38 pjsua_create(); 39 40 41 /* Parse command line arguments: */ 42 if (pjsua_parse_args(argc, argv, &cfg, &uri_to_call) != PJ_SUCCESS) 33 if (app_init(argc, argv) != PJ_SUCCESS) 43 34 return 1; 44 35 45 46 /* Init pjsua */ 47 if (pjsua_init(&cfg, &console_callback) != PJ_SUCCESS) 48 return 1; 49 50 51 /* Start pjsua! */ 52 if (pjsua_start() != PJ_SUCCESS) { 53 pjsua_destroy(); 54 return 1; 55 } 56 57 58 /* Sleep for a while, let any messages get printed to console: */ 59 pj_thread_sleep(500); 60 61 62 /* Start UI console main loop: */ 63 pjsua_console_app_main(&uri_to_call); 64 65 66 /* Destroy pjsua: */ 67 pjsua_destroy(); 68 69 /* This is for internal testing, to make sure that pjsua_destroy() 70 * can be called multiple times. 71 */ 72 pjsua_destroy(); 73 74 75 /* Exit... */ 36 app_main(); 37 app_destroy(); 76 38 77 39 return 0; -
pjproject/trunk/pjsip/build/pjsua_lib.dsp
r476 r503 88 88 # Begin Source File 89 89 90 SOURCE="..\src\pjsua-lib\pjsua_ call.c"90 SOURCE="..\src\pjsua-lib\pjsua_acc.c" 91 91 # End Source File 92 92 # Begin Source File 93 93 94 SOURCE="..\src\pjsua-lib\pjsua_c onsole_app.c"94 SOURCE="..\src\pjsua-lib\pjsua_call.c" 95 95 # End Source File 96 96 # Begin Source File … … 104 104 # Begin Source File 105 105 106 SOURCE="..\src\pjsua-lib\pjsua_ imp.h"106 SOURCE="..\src\pjsua-lib\pjsua_media.c" 107 107 # End Source File 108 108 # Begin Source File 109 109 110 110 SOURCE="..\src\pjsua-lib\pjsua_pres.c" 111 # End Source File112 # Begin Source File113 114 SOURCE="..\src\pjsua-lib\pjsua_reg.c"115 # End Source File116 # Begin Source File117 118 SOURCE="..\src\pjsua-lib\pjsua_settings.c"119 111 # End Source File 120 112 # End Group … … 128 120 # Begin Source File 129 121 130 SOURCE="..\include\pjsua-lib\pjsua_ console_app.h"122 SOURCE="..\include\pjsua-lib\pjsua_internal.h" 131 123 # End Source File 132 124 # End Group -
pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
r492 r503 57 57 */ 58 58 #ifndef PJSUA_MAX_CALLS 59 # define PJSUA_MAX_CALLS 25659 # define PJSUA_MAX_CALLS 32 60 60 #endif 61 61 62 62 63 63 /** 64 * Max ports in the conference bridge. 65 */ 66 #ifndef PJSUA_MAX_CONF_PORTS 67 # define PJSUA_MAX_CONF_PORTS 254 68 #endif 69 70 71 /** 64 72 * Maximum accounts. 65 73 */ 66 74 #ifndef PJSUA_MAX_ACC 67 # define PJSUA_MAX_ACC 3275 # define PJSUA_MAX_ACC 8 68 76 #endif 69 77 70 78 79 /** 80 * Maximum proxies in account. 81 */ 82 #ifndef PJSUA_ACC_MAX_PROXIES 83 # define PJSUA_ACC_MAX_PROXIES 8 84 #endif 85 86 /** 87 * Default registration interval. 88 */ 89 #ifndef PJSUA_REG_INTERVAL 90 # define PJSUA_REG_INTERVAL 55 91 #endif 92 93 94 /** Account identification */ 71 95 typedef int pjsua_acc_id; 96 97 /** Call identification */ 98 typedef int pjsua_call_id; 99 100 /** SIP transport identification */ 101 typedef int pjsua_transport_id; 102 103 /** Buddy identification */ 72 104 typedef int pjsua_buddy_id; 105 106 /** File player identification */ 73 107 typedef int pjsua_player_id; 108 109 /** File recorder identification */ 74 110 typedef int pjsua_recorder_id; 111 112 /** Conference port identification */ 75 113 typedef int pjsua_conf_port_id; 76 114 77 115 116 /** Constant to identify invalid ID for all sorts of IDs. */ 117 #define PJSUA_INVALID_ID (-1) 118 119 120 78 121 /** 79 122 * Account configuration. 80 123 */ 81 struct pjsua_acc_config 82 { 83 /** SIP URL for account ID (mandatory) */ 124 typedef struct pjsua_acc_config 125 { 126 /** 127 * The full SIP URL for the account. The value can take name address or 128 * URL format, and will look something like "sip:account@serviceprovider". 129 * 130 * This field is mandatory. 131 */ 84 132 pj_str_t id; 85 133 86 /** Registrar URI (mandatory) */ 134 /** 135 * This is the URL to be put in the request URI for the registration, 136 * and will look something like "sip:serviceprovider". 137 * 138 * This field should be specified if registration is desired. If the 139 * value is empty, no account registration will be performed. 140 */ 87 141 pj_str_t reg_uri; 88 142 89 /** Optional contact URI */ 143 /** 144 * Optional URI to be put as Contact for this account. It is recommended 145 * that this field is left empty, so that the value will be calculated 146 * automatically based on the transport address. 147 */ 90 148 pj_str_t contact; 91 149 92 /** Service proxy (default: none) */ 93 pj_str_t proxy; 94 95 /** Default timeout (mandatory) */ 96 pj_int32_t reg_timeout; 97 98 /** Number of credentials. */ 150 /** 151 * Number of proxies in the proxy array below. 152 */ 153 unsigned proxy_cnt; 154 155 /** 156 * Optional URI of the proxies to be visited for all outgoing requests 157 * that are using this account (REGISTER, INVITE, etc). Application need 158 * to specify these proxies if the service provider requires that requests 159 * destined towards its network should go through certain proxies first 160 * (for example, border controllers). 161 * 162 * These proxies will be put in the route set for this account, with 163 * maintaining the orders (the first proxy in the array will be visited 164 * first). 165 */ 166 pj_str_t proxy[PJSUA_ACC_MAX_PROXIES]; 167 168 /** 169 * Optional interval for registration, in seconds. If the value is zero, 170 * default interval will be used (PJSUA_REG_INTERVAL, 55 seconds). 171 */ 172 unsigned reg_timeout; 173 174 /** 175 * Number of credentials in the credential array. 176 */ 99 177 unsigned cred_count; 100 178 101 /** Array of credentials. */ 102 pjsip_cred_info cred_info[4]; 103 104 }; 105 106 107 /** 108 * @see pjsua_acc_config 109 */ 110 typedef struct pjsua_acc_config pjsua_acc_config; 111 112 113 /** 114 * PJSUA settings. 115 */ 116 struct pjsua_config 117 { 118 /** SIP UDP signaling port. Set to zero to disable UDP signaling, 119 * which in this case application must manually add a transport 120 * to SIP endpoint. 121 * (default: 5060) 122 */ 123 unsigned udp_port; 124 125 /** Optional hostname or IP address to publish as the host part of 126 * Contact header. This must be specified if UDP transport is 127 * disabled. 128 * (default: NULL) 129 */ 130 pj_str_t sip_host; 131 132 /** Optional port number to publish in the port part of Contact header. 133 * This must be specified if UDP transport is disabled. 134 * (default: 0) 135 */ 136 unsigned sip_port; 137 138 /** Start of RTP port. Set to zero to prevent pjsua from creating 139 * media transports, which in this case application must manually 140 * create media transport for each calls. 141 * (default: 4000) 142 */ 143 unsigned start_rtp_port; 144 145 /** 146 * Enable incoming and outgoing message logging (default: 1). 179 /** 180 * Array of credentials. If registration is desired, normally there should 181 * be at least one credential specified, to successfully authenticate 182 * against the service provider. More credentials can be specified, for 183 * example when the requests are expected to be challenged by the 184 * proxies in the route set. 185 */ 186 pjsip_cred_info cred_info[PJSUA_ACC_MAX_PROXIES]; 187 188 } pjsua_acc_config; 189 190 191 /** 192 * Call this function to initialize account config with default values. 193 * 194 * @param cfg The account config to be initialized. 195 */ 196 PJ_INLINE(void) pjsua_acc_config_default(pjsua_acc_config *cfg) 197 { 198 pj_memset(cfg, 0, sizeof(*cfg)); 199 200 cfg->reg_timeout = PJSUA_REG_INTERVAL; 201 } 202 203 204 205 /** 206 * Account info. Application can query account info by calling 207 * #pjsua_acc_get_info(). 208 */ 209 typedef struct pjsua_acc_info 210 { 211 /** 212 * The account ID. 213 */ 214 pjsua_acc_id id; 215 216 /** 217 * Flag to indicate whether this is the default account. 218 */ 219 pj_bool_t is_default; 220 221 /** 222 * Account URI 223 */ 224 pj_str_t acc_uri; 225 226 /** 227 * Flag to tell whether this account has registration setting 228 * (reg_uri is not empty). 229 */ 230 pj_bool_t has_registration; 231 232 /** 233 * An up to date expiration interval for account registration session. 234 */ 235 int expires; 236 237 /** 238 * Last registration status code. If status code is zero, the account 239 * is currently not registered. Any other value indicates the SIP 240 * status code of the registration. 241 */ 242 pjsip_status_code status; 243 244 /** 245 * String describing the registration status. 246 */ 247 pj_str_t status_text; 248 249 /** 250 * Presence online status for this account. 251 */ 252 pj_bool_t online_status; 253 254 /** 255 * Buffer that is used internally to store the status text. 256 */ 257 char buf_[PJ_ERR_MSG_SIZE]; 258 259 } pjsua_acc_info; 260 261 262 263 /** 264 * STUN configuration. 265 */ 266 typedef struct pjsua_stun_config 267 { 268 /** 269 * The first STUN server IP address or hostname. 270 */ 271 pj_str_t stun_srv1; 272 273 /** 274 * Port number of the first STUN server. 275 * If zero, default STUN port will be used. 276 */ 277 unsigned stun_port1; 278 279 /** 280 * Optional second STUN server IP address or hostname, for which the 281 * result of the mapping request will be compared to. If the value 282 * is empty, only one STUN server will be used. 283 */ 284 pj_str_t stun_srv2; 285 286 /** 287 * Port number of the second STUN server. 288 * If zero, default STUN port will be used. 289 */ 290 unsigned stun_port2; 291 292 } pjsua_stun_config; 293 294 295 296 /** 297 * Call this function to initialize STUN config with default values. 298 * 299 * @param cfg The STUN config to be initialized. 300 */ 301 PJ_INLINE(void) pjsua_stun_config_default(pjsua_stun_config *cfg) 302 { 303 pj_memset(cfg, 0, sizeof(*cfg)); 304 } 305 306 307 /** 308 * Transport configuration for creating UDP transports for both SIP 309 * and media. 310 */ 311 typedef struct pjsua_transport_config 312 { 313 /** 314 * UDP port number to bind locally. This setting MUST be specified 315 * even when default port is desired. If the value is zero, the 316 * transport will be bound to any available port, and application 317 * can query the port by querying the transport info. 318 */ 319 unsigned port; 320 321 /** 322 * Optional address where the socket should be bound. 323 */ 324 pj_in_addr ip_addr; 325 326 /** 327 * Flag to indicate whether STUN should be used. 328 */ 329 pj_bool_t use_stun; 330 331 /** 332 * STUN configuration, must be specified when STUN is used. 333 */ 334 pjsua_stun_config stun_config; 335 336 } pjsua_transport_config; 337 338 339 /** 340 * Call this function to initialize UDP config with default values. 341 * 342 * @param cfg The UDP config to be initialized. 343 */ 344 PJ_INLINE(void) pjsua_transport_config_default(pjsua_transport_config *cfg) 345 { 346 pj_memset(cfg, 0, sizeof(*cfg)); 347 } 348 349 350 /** 351 * Normalize STUN config. 352 */ 353 PJ_INLINE(void) pjsua_normalize_stun_config( pjsua_stun_config *cfg ) 354 { 355 if (cfg->stun_srv1.slen) { 356 357 if (cfg->stun_port1 == 0) 358 cfg->stun_port1 = 3478; 359 360 if (cfg->stun_srv2.slen == 0) { 361 cfg->stun_srv2 = cfg->stun_srv1; 362 cfg->stun_port2 = cfg->stun_port1; 363 } else { 364 if (cfg->stun_port2 == 0) 365 cfg->stun_port2 = 3478; 366 } 367 368 } else { 369 cfg->stun_port1 = 0; 370 cfg->stun_srv2.slen = 0; 371 cfg->stun_port2 = 0; 372 } 373 } 374 375 376 /** 377 * Duplicate transport config. 378 */ 379 PJ_INLINE(void) pjsua_transport_config_dup(pj_pool_t *pool, 380 pjsua_transport_config *dst, 381 const pjsua_transport_config *src) 382 { 383 pj_memcpy(dst, src, sizeof(*src)); 384 385 if (src->stun_config.stun_srv1.slen) { 386 pj_strdup_with_null(pool, &dst->stun_config.stun_srv1, 387 &src->stun_config.stun_srv1); 388 } 389 390 if (src->stun_config.stun_srv2.slen) { 391 pj_strdup_with_null(pool, &dst->stun_config.stun_srv2, 392 &src->stun_config.stun_srv2); 393 } 394 395 pjsua_normalize_stun_config(&dst->stun_config); 396 } 397 398 399 400 /** 401 * Transport info. 402 */ 403 typedef struct pjsua_transport_info 404 { 405 /** 406 * PJSUA transport identification. 407 */ 408 pjsua_transport_id id; 409 410 /** 411 * Transport type. 412 */ 413 pjsip_transport_type_e type; 414 415 /** 416 * Transport type name. 417 */ 418 pj_str_t type_name; 419 420 /** 421 * Transport string info/description. 422 */ 423 pj_str_t info; 424 425 /** 426 * Transport flag (see ##pjsip_transport_flags_e). 427 */ 428 unsigned flag; 429 430 /** 431 * Local address length. 432 */ 433 unsigned addr_len; 434 435 /** 436 * Local/bound address. 437 */ 438 pj_sockaddr local_addr; 439 440 /** 441 * Published address (or transport address name). 442 */ 443 pjsip_host_port local_name; 444 445 /** 446 * Current number of objects currently referencing this transport. 447 */ 448 unsigned usage_count; 449 450 451 } pjsua_transport_info; 452 453 454 /** 455 * Media configuration. 456 */ 457 typedef struct pjsua_media_config 458 { 459 /** 460 * Clock rate to be applied to the conference bridge. 461 * If value is zero, default clock rate will be used (16KHz). 462 */ 463 unsigned clock_rate; 464 465 /** 466 * Specify maximum number of media ports to be created in the 467 * conference bridge. Since all media terminate in the bridge 468 * (calls, file player, file recorder, etc), the value must be 469 * large enough to support all of them. However, the larger 470 * the value, the more computations are performed. 471 */ 472 unsigned max_media_ports; 473 474 /** 475 * Specify whether the media manager should manage its own 476 * ioqueue for the RTP/RTCP sockets. If yes, ioqueue will be created 477 * and at least one worker thread will be created too. If no, 478 * the RTP/RTCP sockets will share the same ioqueue as SIP sockets, 479 * and no worker thread is needed. 480 * 481 * Normally application would say yes here, unless it wants to 482 * run everything from a single thread. 483 */ 484 pj_bool_t has_ioqueue; 485 486 /** 487 * Specify the number of worker threads to handle incoming RTP 488 * packets. A value of one is recommended for most applications. 489 */ 490 unsigned thread_cnt; 491 492 493 } pjsua_media_config; 494 495 496 /** 497 * Use this function to initialize media config. 498 * 499 * @param cfg The media config to be initialized. 500 */ 501 PJ_INLINE(void) pjsua_media_config_default(pjsua_media_config *cfg) 502 { 503 pj_memset(cfg, 0, sizeof(*cfg)); 504 505 cfg->clock_rate = 16000; 506 cfg->max_media_ports = 32; 507 cfg->has_ioqueue = PJ_TRUE; 508 cfg->thread_cnt = 1; 509 } 510 511 512 513 /** 514 * Logging configuration. 515 */ 516 typedef struct pjsua_logging_config 517 { 518 /** 519 * Log incoming and outgoing SIP message? Yes! 147 520 */ 148 521 pj_bool_t msg_logging; 149 522 150 /** Maximum calls to support (default: 4) */ 151 unsigned max_calls; 152 153 /** Maximum slots in the conference bridge (default: 0/calculated 154 * as max_calls*2 155 */ 156 unsigned conf_ports; 157 158 /** Number of worker threads (value >=0, default: 1) */ 159 unsigned thread_cnt; 160 161 /** Separate ioqueue for media? (default: yes) */ 162 pj_bool_t media_has_ioqueue; 163 164 /** Number of worker thread for media (value >=0, default: 1) */ 165 unsigned media_thread_cnt; 166 167 /** First STUN server IP address. When STUN is configured, then the 168 * two STUN server settings must be fully set. 169 * (default: none) 170 */ 171 pj_str_t stun_srv1; 172 173 /** First STUN port number */ 174 unsigned stun_port1; 175 176 /** Second STUN server IP address */ 177 pj_str_t stun_srv2; 178 179 /** Second STUN server port number */ 180 unsigned stun_port2; 181 182 /** Sound player device ID (default: 0) */ 183 unsigned snd_player_id; 184 185 /** Sound capture device ID (default: 0) */ 186 unsigned snd_capture_id; 187 188 /** Internal clock rate (to be applied to sound devices and conference 189 * bridge, default is 0/follows the codec, or 44100 for MacOS). 190 */ 191 unsigned clock_rate; 192 193 /** Do not use sound device (default: 0). */ 194 pj_bool_t null_audio; 195 196 /** WAV file to load for auto_play (default: NULL) */ 197 pj_str_t wav_file; 198 199 /** Auto play WAV file for calls? (default: no) */ 200 pj_bool_t auto_play; 201 202 /** Auto loopback calls? (default: no) */ 203 pj_bool_t auto_loop; 204 205 /** Automatically put calls to conference? (default: no) */ 206 pj_bool_t auto_conf; 207 208 /** Speex codec complexity? (default: 10) */ 209 unsigned complexity; 210 211 /** Speex codec quality? (default: 10) */ 212 unsigned quality; 213 214 /** Codec ptime? (default: 0 (follows the codec)) */ 215 unsigned ptime; 216 217 /** Number of additional codecs/"--add-codec" with pjsua (default: 0) */ 218 unsigned codec_cnt; 219 220 /** Additional codecs/"--add-codec" options */ 221 pj_str_t codec_arg[32]; 222 223 /** SIP status code to be automatically sent to incoming calls 224 * (default: 100). 225 */ 226 unsigned auto_answer; 227 228 /** Periodic time to refresh call with re-INVITE (default: 0) 229 */ 230 unsigned uas_refresh; 231 232 /** Maximum incoming call duration (default: 3600) */ 233 unsigned uas_duration; 234 235 /** Outbound proxy (default: none) */ 236 pj_str_t outbound_proxy; 237 238 /** Number of SIP accounts */ 239 unsigned acc_cnt; 240 241 /** SIP accounts configuration */ 242 pjsua_acc_config acc_config[32]; 243 244 /** Logging verbosity (default: 5). */ 245 unsigned log_level; 246 247 /** Logging to be displayed to stdout (default: 4) */ 248 unsigned app_log_level; 249 250 /** Log decoration */ 251 unsigned log_decor; 252 253 /** Optional log filename (default: NULL) */ 523 /** 524 * Input verbosity level. Value 5 is reasonable. 525 */ 526 unsigned level; 527 528 /** 529 * Verbosity level for console. Value 4 is reasonable. 530 */ 531 unsigned console_level; 532 533 /** 534 * Log decoration. 535 */ 536 unsigned decor; 537 538 /** 539 * Optional log filename. 540 */ 254 541 pj_str_t log_filename; 255 542 256 /** Number of buddies in address book (default: 0) */ 257 unsigned buddy_cnt; 258 259 /** Buddies URI */ 260 pj_str_t buddy_uri[256]; 261 }; 262 263 264 /** 265 * @see pjsua_config 266 */ 267 typedef struct pjsua_config pjsua_config; 268 543 /** 544 * Optional callback function to be called to write log to 545 * application specific device. This function will be called for 546 * log messages on input verbosity level. 547 */ 548 void (*cb)(int level, const char *data, pj_size_t len); 549 550 551 } pjsua_logging_config; 552 553 554 /** 555 * Use this function to initialize logging config. 556 * 557 * @param cfg The logging config to be initialized. 558 */ 559 PJ_INLINE(void) pjsua_logging_config_default(pjsua_logging_config *cfg) 560 { 561 pj_memset(cfg, 0, sizeof(*cfg)); 562 563 cfg->msg_logging = PJ_TRUE; 564 cfg->level = 5; 565 cfg->console_level = 4; 566 cfg->decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME | 567 PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE; 568 } 569 570 /** 571 * Use this function to duplicate logging config. 572 * 573 * @param pool Pool to use. 574 * @param dst Destination config. 575 * @param src Source config. 576 */ 577 PJ_INLINE(void) pjsua_logging_config_dup(pj_pool_t *pool, 578 pjsua_logging_config *dst, 579 const pjsua_logging_config *src) 580 { 581 pj_memcpy(dst, src, sizeof(*src)); 582 pj_strdup_with_null(pool, &dst->log_filename, &src->log_filename); 583 } 584 585 586 /** 587 * Buddy configuration. 588 */ 589 typedef struct pjsua_buddy_config 590 { 591 /** 592 * Buddy URL or name address. 593 */ 594 pj_str_t uri; 595 596 /** 597 * Specify whether presence subscription should start immediately. 598 */ 599 pj_bool_t subscribe; 600 601 } pjsua_buddy_config; 602 603 604 /** 605 * Buddy's online status. 606 */ 607 typedef enum pjsua_buddy_status 608 { 609 /** 610 * Online status is unknown (possibly because no presence subscription 611 * has been established). 612 */ 613 PJSUA_BUDDY_STATUS_UNKNOWN, 614 615 /** 616 * Buddy is known to be offline. 617 */ 618 PJSUA_BUDDY_STATUS_ONLINE, 619 620 /** 621 * Buddy is offline. 622 */ 623 PJSUA_BUDDY_STATUS_OFFLINE, 624 625 } pjsua_buddy_status; 626 627 628 629 /** 630 * Buddy info. 631 */ 632 typedef struct pjsua_buddy_info 633 { 634 /** 635 * The buddy ID. 636 */ 637 pjsua_buddy_id id; 638 639 /** 640 * The full URI of the buddy, as specified in the configuration. 641 */ 642 pj_str_t uri; 643 644 /** 645 * Buddy's Contact, only available when presence subscription has 646 * been established to the buddy. 647 */ 648 pj_str_t contact; 649 650 /** 651 * Buddy's online status. 652 */ 653 pjsua_buddy_status status; 654 655 /** 656 * Text to describe buddy's online status. 657 */ 658 pj_str_t status_text; 659 660 /** 661 * Flag to indicate that we should monitor the presence information for 662 * this buddy (normally yes, unless explicitly disabled). 663 */ 664 pj_bool_t monitor_pres; 665 666 /** 667 * Internal buffer. 668 */ 669 char buf_[256]; 670 671 } pjsua_buddy_info; 672 673 674 /** 675 * Codec config. 676 */ 677 typedef struct pjsua_codec_info 678 { 679 /** 680 * Codec unique identification. 681 */ 682 pj_str_t codec_id; 683 684 /** 685 * Codec priority (integer 0-255). 686 */ 687 pj_uint8_t priority; 688 689 /** 690 * Internal buffer. 691 */ 692 char buf_[32]; 693 694 } pjsua_codec_info; 269 695 270 696 … … 272 698 * Application callbacks. 273 699 */ 274 struct pjsua_callback700 typedef struct pjsua_callback 275 701 { 276 702 /** … … 279 705 * detail call states. 280 706 */ 281 void (*on_call_state)( int call_index, pjsip_event *e);707 void (*on_call_state)(pjsua_call_id call_id, pjsip_event *e); 282 708 283 709 /** 284 710 * Notify application on incoming call. 285 711 */ 286 void (*on_incoming_call)(pjsua_acc_id acc_id, int call_index,712 void (*on_incoming_call)(pjsua_acc_id acc_id, pjsua_call_id call_id, 287 713 pjsip_rx_data *rdata); 714 715 /** 716 * Notify application when media state in the call has changed. 717 * Normal application would need to implement this callback, e.g. 718 * to connect the call's media to sound device. 719 */ 720 void (*on_call_media_state)(pjsua_call_id call_id); 288 721 289 722 /** … … 294 727 * transfer. 295 728 */ 296 void (*on_call_transfered)( int call_index,729 void (*on_call_transfered)(pjsua_call_id call_id, 297 730 const pj_str_t *dst, 298 731 pjsip_status_code *code); … … 313 746 /** 314 747 * Notify application on incoming pager (i.e. MESSAGE request). 315 * Argument call_i ndex will be -1 if MESSAGE request is not related to an748 * Argument call_id will be -1 if MESSAGE request is not related to an 316 749 * existing call. 317 750 */ 318 void (*on_pager)(int call_index, const pj_str_t *from, 319 const pj_str_t *to, const pj_str_t *txt); 751 void (*on_pager)(pjsua_call_id call_id, const pj_str_t *from, 752 const pj_str_t *to, const pj_str_t *contact, 753 const pj_str_t *mime_type, const pj_str_t *body); 754 755 /** 756 * Notify application about the delivery status of outgoing pager 757 * request. 758 * 759 * @param call_id Containts the ID of the call where the IM was 760 * sent, or PJSUA_INVALID_ID if the IM was sent 761 * outside call context. 762 * @param to Destination URI. 763 * @param body Message body. 764 * @param user_data Arbitrary data that was specified when sending 765 * IM message. 766 * @param status Delivery status. 767 * @param reason Delivery status reason. 768 */ 769 void (*on_pager_status)(pjsua_call_id call_id, 770 const pj_str_t *to, 771 const pj_str_t *body, 772 void *user_data, 773 pjsip_status_code status, 774 const pj_str_t *reason); 320 775 321 776 /** 322 777 * Notify application about typing indication. 323 778 */ 324 void (*on_typing)(int call_index, const pj_str_t *from, 325 const pj_str_t *to, pj_bool_t is_typing); 326 327 }; 328 329 /** 330 * @see pjsua_callback 331 */ 332 typedef struct pjsua_callback pjsua_callback; 779 void (*on_typing)(pjsua_call_id call_id, const pj_str_t *from, 780 const pj_str_t *to, const pj_str_t *contact, 781 pj_bool_t is_typing); 782 783 } pjsua_callback; 784 785 786 787 788 /** 789 * PJSUA settings. 790 */ 791 typedef struct pjsua_config 792 { 793 794 /** 795 * Maximum calls to support (default: 4) 796 */ 797 unsigned max_calls; 798 799 /** 800 * Number of worker threads. Normally application will want to have at 801 * least one worker thread, unless when it wants to poll the library 802 * periodically, which in this case the worker thread can be set to 803 * zero. 804 */ 805 unsigned thread_cnt; 806 807 /** 808 * Number of outbound proxies in the array. 809 */ 810 unsigned outbound_proxy_cnt; 811 812 /** 813 * Specify the URL of outbound proxies to visit for all outgoing requests. 814 * The outbound proxies will be used for all accounts, and it will 815 * be used to build the route set for outgoing requests. The final 816 * route set for outgoing requests will consists of the outbound proxies 817 * and the proxy configured in the account. 818 */ 819 pj_str_t outbound_proxy[4]; 820 821 /** 822 * Number of credentials in the credential array. 823 */ 824 unsigned cred_count; 825 826 /** 827 * Array of credentials. These credentials will be used by all accounts, 828 * and can be used to authenticate against outbound proxies. 829 */ 830 pjsip_cred_info cred_info[PJSUA_ACC_MAX_PROXIES]; 831 832 /** 833 * Application callback. 834 */ 835 pjsua_callback cb; 836 837 } pjsua_config; 838 839 840 /** 841 * Use this function to initialize pjsua config. 842 * 843 * @param cfg pjsua config to be initialized. 844 */ 845 PJ_INLINE(void) pjsua_config_default(pjsua_config *cfg) 846 { 847 pj_memset(cfg, 0, sizeof(*cfg)); 848 849 cfg->max_calls = 4; 850 cfg->thread_cnt = 1; 851 } 852 853 854 /** 855 * Duplicate credential. 856 */ 857 PJ_INLINE(void) pjsip_cred_dup( pj_pool_t *pool, 858 pjsip_cred_info *dst, 859 const pjsip_cred_info *src) 860 { 861 pj_strdup_with_null(pool, &dst->realm, &src->realm); 862 pj_strdup_with_null(pool, &dst->scheme, &src->scheme); 863 pj_strdup_with_null(pool, &dst->username, &src->username); 864 pj_strdup_with_null(pool, &dst->data, &src->data); 865 866 } 867 868 869 /** 870 * Duplicate pjsua_config. 871 */ 872 PJ_INLINE(void) pjsua_config_dup(pj_pool_t *pool, 873 pjsua_config *dst, 874 const pjsua_config *src) 875 { 876 unsigned i; 877 878 pj_memcpy(dst, src, sizeof(*src)); 879 880 for (i=0; i<src->outbound_proxy_cnt; ++i) { 881 pj_strdup_with_null(pool, &dst->outbound_proxy[i], 882 &src->outbound_proxy[i]); 883 } 884 885 for (i=0; i<src->cred_count; ++i) { 886 pjsip_cred_dup(pool, &dst->cred_info[i], &src->cred_info[i]); 887 } 888 } 889 890 891 /** 892 * Call media status. 893 */ 894 typedef enum pjsua_call_media_status 895 { 896 PJSUA_CALL_MEDIA_NONE, 897 PJSUA_CALL_MEDIA_ACTIVE, 898 PJSUA_CALL_MEDIA_LOCAL_HOLD, 899 PJSUA_CALL_MEDIA_REMOTE_HOLD, 900 } pjsua_call_media_status; 333 901 334 902 … … 336 904 * Call info. 337 905 */ 338 struct pjsua_call_info 339 { 340 unsigned index; 341 pj_bool_t active; 906 typedef struct pjsua_call_info 907 { 908 /** Call identification. */ 909 pjsua_call_id id; 910 911 /** Initial call role (UAC == caller) */ 342 912 pjsip_role_e role; 913 914 /** Local URI */ 343 915 pj_str_t local_info; 916 917 /** Local Contact */ 918 pj_str_t local_contact; 919 920 /** Remote URI */ 344 921 pj_str_t remote_info; 922 923 /** Remote contact */ 924 pj_str_t remote_contact; 925 926 /** Dialog Call-ID string. */ 927 pj_str_t call_id; 928 929 /** Call state */ 345 930 pjsip_inv_state state; 931 932 /** Text describing the state */ 346 933 pj_str_t state_text; 934 935 /** Last status code heard, which can be used as cause code */ 347 936 pjsip_status_code last_status; 937 938 /** The reason phrase describing the status. */ 348 939 pj_str_t last_status_text; 940 941 /** Call media status. */ 942 pjsua_call_media_status media_status; 943 944 /** Media direction */ 945 pjmedia_dir media_dir; 946 947 /** The conference port number for the call */ 948 pjsua_conf_port_id conf_slot; 949 950 /** Up-to-date call connected duration (zero when call is not 951 * established) 952 */ 349 953 pj_time_val connect_duration; 954 955 /** Total call duration, including set-up time */ 350 956 pj_time_val total_duration; 351 pj_bool_t has_media; 352 pjsua_conf_port_id conf_slot; 353 }; 354 355 typedef struct pjsua_call_info pjsua_call_info; 356 357 358 enum pjsua_buddy_status 359 { 360 PJSUA_BUDDY_STATUS_UNKNOWN, 361 PJSUA_BUDDY_STATUS_ONLINE, 362 PJSUA_BUDDY_STATUS_OFFLINE, 363 }; 364 365 typedef enum pjsua_buddy_status pjsua_buddy_status; 366 367 368 /** 369 * Buddy info. 370 */ 371 struct pjsua_buddy_info 372 { 373 pjsua_buddy_id index; 374 pj_bool_t is_valid; 957 958 /** Internal */ 959 struct { 960 char local_info[128]; 961 char local_contact[128]; 962 char remote_info[128]; 963 char remote_contact[128]; 964 char call_id[128]; 965 char last_status_text[128]; 966 } buf_; 967 968 } pjsua_call_info; 969 970 971 972 973 /** 974 * Conference port info. 975 */ 976 typedef struct pjsua_conf_port_info 977 { 978 /** Conference port number. */ 979 pjsua_conf_port_id slot_id; 980 981 /** Port name. */ 375 982 pj_str_t name; 376 pj_str_t display_name; 377 pj_str_t host; 378 unsigned port; 379 pj_str_t uri; 380 pjsua_buddy_status status; 381 pj_str_t status_text; 382 pj_bool_t monitor; 383 }; 384 385 typedef struct pjsua_buddy_info pjsua_buddy_info; 386 387 388 /** 389 * Account info. 390 */ 391 struct pjsua_acc_info 392 { 393 pjsua_acc_id index; 394 pj_str_t acc_id; 395 pj_bool_t has_registration; 396 int expires; 397 pjsip_status_code status; 398 pj_str_t status_text; 399 pj_bool_t online_status; 400 char buf[PJ_ERR_MSG_SIZE]; 401 }; 402 403 typedef struct pjsua_acc_info pjsua_acc_info; 404 405 406 /** 407 * Conference port info. 408 */ 409 struct pjsua_conf_port_info 410 { 411 pjsua_conf_port_id slot_id; 412 pj_str_t name; 983 984 /** Clock rate. */ 413 985 unsigned clock_rate; 986 987 /** Number of channels. */ 414 988 unsigned channel_count; 989 990 /** Samples per frame */ 415 991 unsigned samples_per_frame; 992 993 /** Bits per sample */ 416 994 unsigned bits_per_sample; 995 996 /** Number of listeners in the array. */ 417 997 unsigned listener_cnt; 418 pjsua_conf_port_id listeners[256]; 419 }; 420 421 422 typedef struct pjsua_conf_port_info pjsua_conf_port_info; 998 999 /** Array of listeners (in other words, ports where this port is 1000 * transmitting to. 1001 */ 1002 pjsua_conf_port_id listeners[PJSUA_MAX_CONF_PORTS]; 1003 1004 } pjsua_conf_port_info; 1005 1006 1007 /** 1008 * This structure holds information about custom media transport to 1009 * be registered to pjsua. 1010 */ 1011 typedef struct pjsua_media_transport 1012 { 1013 /** 1014 * Media socket information containing the address information 1015 * of the RTP and RTCP socket. 1016 */ 1017 pjmedia_sock_info skinfo; 1018 1019 /** 1020 * The media transport instance. 1021 */ 1022 pjmedia_transport *transport; 1023 1024 } pjsua_media_transport; 1025 1026 1027 /** 1028 * This structure describes additional information to be sent with 1029 * outgoing SIP message. 1030 */ 1031 typedef struct pjsua_msg_data 1032 { 1033 /** 1034 * Additional message headers as linked list. 1035 */ 1036 pjsip_hdr hdr_list; 1037 1038 /** 1039 * MIME type of optional message body. 1040 */ 1041 pj_str_t content_type; 1042 1043 /** 1044 * Optional message body. 1045 */ 1046 pj_str_t msg_body; 1047 1048 } pjsua_msg_data; 1049 1050 1051 /** 1052 * Initialize message data. 1053 * 1054 * @param msg_data Message data to be initialized. 1055 */ 1056 PJ_INLINE(void) pjsua_msg_data_init(pjsua_msg_data *msg_data) 1057 { 1058 pj_memset(msg_data, 0, sizeof(*msg_data)); 1059 pj_list_init(&msg_data->hdr_list); 1060 } 423 1061 424 1062 425 1063 /***************************************************************************** 426 * PJSUA API (defined in pjsua_core.c). 427 */ 428 429 /** 430 * Initialize pjsua settings with default parameters. 431 */ 432 PJ_DECL(void) pjsua_default_config(pjsua_config *cfg); 433 434 435 /** 436 * Validate configuration. 437 */ 438 PJ_DECL(pj_status_t) pjsua_test_config(const pjsua_config *cfg, 439 char *errmsg, 440 int len); 441 442 443 /** 444 * Instantiate pjsua application. This initializes pjlib/pjlib-util, and 445 * creates memory pool factory to be used by application. 1064 * PJSUA Core API 1065 */ 1066 1067 1068 /** 1069 * Instantiate pjsua application. Application must call this function before 1070 * calling any other functions, to make sure that the underlying libraries 1071 * are properly initialized. Once this function has returned success, 1072 * application must call pjsua_destroy() before quitting. 1073 * 1074 * @return PJ_SUCCESS on success, or the appropriate error code. 446 1075 */ 447 1076 PJ_DECL(pj_status_t) pjsua_create(void); … … 449 1078 450 1079 /** 451 * Initialize pjsua application with the specified settings. 452 * 453 * This will initialize all libraries, create endpoint instance, and register 454 * pjsip modules. 455 * 456 * Application may register module after calling this function. 457 */ 458 PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg, 459 const pjsua_callback *cb); 460 461 462 /** 463 * Start pjsua stack. Application calls this after pjsua settings has been 464 * configured. 465 * 466 * This will start the transport, worker threads (if any), and registration 467 * process, if registration is configured. 1080 * Initialize pjsua with the specified settings. All the settings are 1081 * optional, and the default values will be used when the config is not 1082 * specified. 1083 * 1084 * @param ua_cfg User agent configuration. 1085 * @param log_cfg Optional logging configuration. 1086 * @param media_cfg Optional media configuration. 1087 * 1088 * @return PJ_SUCCESS on success, or the appropriate error code. 1089 */ 1090 PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *ua_cfg, 1091 const pjsua_logging_config *log_cfg, 1092 const pjsua_media_config *media_cfg); 1093 1094 1095 /** 1096 * Application is recommended to call this function after all initialization 1097 * is done, so that the library can do additional checking set up 1098 * additional 1099 * 1100 * @return PJ_SUCCESS on success, or the appropriate error code. 468 1101 */ 469 1102 PJ_DECL(pj_status_t) pjsua_start(void); 470 1103 471 /** 472 * Destroy pjsua. 1104 1105 /** 1106 * Destroy pjsua. This function must be called once PJSUA is created. To 1107 * make it easier for application, application may call this function 1108 * several times with no danger. 1109 * 1110 * @return PJ_SUCCESS on success, or the appropriate error code. 473 1111 */ 474 1112 PJ_DECL(pj_status_t) pjsua_destroy(void); 475 1113 476 /** 477 * Poll pjsua. 1114 1115 /** 1116 * Poll pjsua for events, and if necessary block the caller thread for 1117 * the specified maximum interval (in miliseconds). 1118 * 1119 * @param msec_timeout Maximum time to wait, in miliseconds. 1120 * 1121 * @return The number of events that have been handled during the 1122 * poll. Negative value indicates error, and application 1123 * can retrieve the error as (err = -return_value). 478 1124 */ 479 1125 PJ_DECL(int) pjsua_handle_events(unsigned msec_timeout); … … 481 1127 482 1128 /** 483 * Get SIP endpoint instance. 484 * Only valid after pjsua_init(). 1129 * Create memory pool. 1130 * 1131 * @param name Optional pool name. 1132 * @param size Initial size of the pool. 1133 * @param increment Increment size. 1134 * 1135 * @return The pool, or NULL when there's no memory. 1136 */ 1137 PJ_DECL(pj_pool_t*) pjsua_pool_create(const char *name, pj_size_t init_size, 1138 pj_size_t increment); 1139 1140 1141 /** 1142 * Application can call this function at any time (after pjsua_create(), of 1143 * course) to change logging settings. 1144 * 1145 * @param c Logging configuration. 1146 * 1147 * @return PJ_SUCCESS on success, or the appropriate error code. 1148 */ 1149 PJ_DECL(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *c); 1150 1151 1152 /** 1153 * Internal function to get SIP endpoint instance of pjsua, which is 1154 * needed for example to register module, create transports, etc. 1155 * Probably is only valid after #pjsua_init() is called. 1156 * 1157 * @return SIP endpoint instance. 485 1158 */ 486 1159 PJ_DECL(pjsip_endpoint*) pjsua_get_pjsip_endpt(void); 487 1160 488 1161 /** 489 * Get media endpoint instance. 490 * Only valid after pjsua_init(). 1162 * Internal function to get media endpoint instance. 1163 * Only valid after #pjsua_init() is called. 1164 * 1165 * @return Media endpoint instance. 491 1166 */ 492 1167 PJ_DECL(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void); 493 1168 494 /**495 * Replace media transport.496 */497 PJ_DECL(pj_status_t) pjsua_set_call_media_transport(unsigned call_index,498 const pjmedia_sock_info *i,499 pjmedia_transport *tp);500 501 1169 502 1170 /***************************************************************************** 503 * PJSUA Call API (defined in pjsua_call.c). 1171 * PJSUA SIP Transport API. 1172 */ 1173 1174 /** 1175 * Create SIP transport. 1176 * 1177 * @param type Transport type. 1178 * @param cfg Transport configuration. 1179 * @param p_id Optional pointer to receive transport ID. 1180 * 1181 * @return PJ_SUCCESS on success, or the appropriate error code. 1182 */ 1183 PJ_DECL(pj_status_t) pjsua_transport_create(pjsip_transport_type_e type, 1184 const pjsua_transport_config *cfg, 1185 pjsua_transport_id *p_id); 1186 1187 /** 1188 * Register transport that has been created by application. 1189 * 1190 * @param tp Transport instance. 1191 * @param p_id Optional pointer to receive transport ID. 1192 * 1193 * @return PJ_SUCCESS on success, or the appropriate error code. 1194 */ 1195 PJ_DECL(pj_status_t) pjsua_transport_register(pjsip_transport *tp, 1196 pjsua_transport_id *p_id); 1197 1198 1199 /** 1200 * Enumerate all transports currently created in the system. 1201 * 1202 * @param id Array to receive transport ids. 1203 * @param count In input, specifies the maximum number of elements. 1204 * On return, it contains the actual number of elements. 1205 * 1206 * @return PJ_SUCCESS on success, or the appropriate error code. 1207 */ 1208 PJ_DECL(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[], 1209 unsigned *count ); 1210 1211 1212 /** 1213 * Get information about transports. 1214 * 1215 * @param id Transport ID. 1216 * @param info Pointer to receive transport info. 1217 * 1218 * @return PJ_SUCCESS on success, or the appropriate error code. 1219 */ 1220 PJ_DECL(pj_status_t) pjsua_transport_get_info(pjsua_transport_id id, 1221 pjsua_transport_info *info); 1222 1223 1224 /** 1225 * Disable a transport or re-enable it. By default transport is always 1226 * enabled after it is created. Disabling a transport does not necessarily 1227 * close the socket, it will only discard incoming messages and prevent 1228 * the transport from being used to send outgoing messages. 1229 * 1230 * @param id Transport ID. 1231 * @param enabled Non-zero to enable, zero to disable. 1232 * 1233 * @return PJ_SUCCESS on success, or the appropriate error code. 1234 */ 1235 PJ_DECL(pj_status_t) pjsua_transport_set_enable(pjsua_transport_id id, 1236 pj_bool_t enabled); 1237 1238 1239 /** 1240 * Close the transport. If transport is forcefully closed, it will be 1241 * immediately closed, and any pending transactions that are using the 1242 * transport may not terminate properly. Otherwise, the system will wait 1243 * until all transactions are closed while preventing new users from 1244 * using the transport, and will close the transport when it is safe to 1245 * do so. 1246 * 1247 * @param id Transport ID. 1248 * @param force Non-zero to immediately close the transport. This 1249 * is not recommended! 1250 * 1251 * @return PJ_SUCCESS on success, or the appropriate error code. 1252 */ 1253 PJ_DECL(pj_status_t) pjsua_transport_close( pjsua_transport_id id, 1254 pj_bool_t force ); 1255 1256 1257 /***************************************************************************** 1258 * PJSUA Media Transport. 1259 */ 1260 1261 /** 1262 * Create UDP media transports for all the calls. This function creates 1263 * one UDP media transport for each call. 1264 * 1265 * @param cfg Media transport configuration. The "port" field in the 1266 * configuration is used as the start port to bind the 1267 * sockets. 1268 * 1269 * @return PJ_SUCCESS on success, or the appropriate error code. 1270 */ 1271 PJ_DECL(pj_status_t) 1272 pjsua_media_transports_create(const pjsua_transport_config *cfg); 1273 1274 1275 /** 1276 * Register custom media transports to be used by calls. There must 1277 * enough media transports for all calls. 1278 * 1279 * @param tp The media transport array. 1280 * @param count Number of elements in the array. This number MUST 1281 * match the number of maximum calls configured when 1282 * pjsua is created. 1283 * @param auto_delete Flag to indicate whether the transports should be 1284 * destroyed when pjsua is shutdown. 1285 * 1286 * @return PJ_SUCCESS on success, or the appropriate error code. 1287 */ 1288 PJ_DECL(pj_status_t) 1289 pjsua_media_transports_attach( pjsua_media_transport tp[], 1290 unsigned count, 1291 pj_bool_t auto_delete); 1292 1293 1294 1295 /***************************************************************************** 1296 * PJSUA Call API. 504 1297 */ 505 1298 506 1299 /** 507 1300 * Get maximum number of calls configured in pjsua. 1301 * 1302 * @return Maximum number of calls configured. 508 1303 */ 509 1304 PJ_DECL(unsigned) pjsua_call_get_max_count(void); 510 1305 511 1306 /** 512 * Get current number of active calls. 1307 * Get number of currently active calls. 1308 * 1309 * @return Number of currently active calls. 513 1310 */ 514 1311 PJ_DECL(unsigned) pjsua_call_get_count(void); 1312 1313 /** 1314 * Enumerate all active calls. 1315 * 1316 * @param ids Array of account IDs to be initialized. 1317 * @param count In input, specifies the maximum number of elements. 1318 * On return, it contains the actual number of elements. 1319 * 1320 * @return PJ_SUCCESS on success, or the appropriate error code. 1321 */ 1322 PJ_DECL(pj_status_t) pjsua_enum_calls(pjsua_call_id ids[], 1323 unsigned *count); 1324 1325 1326 /** 1327 * Make outgoing call to the specified URI using the specified account. 1328 * 1329 * @param acc_id The account to be used. 1330 * @param target URI to be put in the request URI. 1331 * @param dst_uri URI to be put in the To header (normally is the same 1332 * as the target URI). 1333 * @param options Options (must be zero at the moment). 1334 * @param user_data Arbitrary user data to be attached to the call, and 1335 * can be retrieved later. 1336 * @param msg_data Optional headers etc to be added to outgoing INVITE 1337 * request, or NULL if no custom header is desired. 1338 * @param p_call_id Pointer to receive call identification. 1339 * 1340 * @return PJ_SUCCESS on success, or the appropriate error code. 1341 */ 1342 PJ_DECL(pj_status_t) pjsua_call_make_call(pjsua_acc_id acc_id, 1343 const pj_str_t *dst_uri, 1344 unsigned options, 1345 void *user_data, 1346 const pjsua_msg_data *msg_data, 1347 pjsua_call_id *p_call_id); 1348 515 1349 516 1350 /** 517 1351 * Check if the specified call has active INVITE session and the INVITE 518 1352 * session has not been disconnected. 519 */ 520 PJ_DECL(pj_bool_t) pjsua_call_is_active(unsigned call_index); 521 522 523 /** 524 * Check if call has a media session. 525 */ 526 PJ_DECL(pj_bool_t) pjsua_call_has_media(unsigned call_index); 527 528 529 /** 530 * Get call info. 531 */ 532 PJ_DECL(pj_status_t) pjsua_call_get_info(unsigned call_index, 1353 * 1354 * @param call_id Call identification. 1355 * 1356 * @return Non-zero if call is active. 1357 */ 1358 PJ_DECL(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id); 1359 1360 1361 /** 1362 * Check if call has an active media session. 1363 * 1364 * @param call_id Call identification. 1365 * 1366 * @return Non-zero if yes. 1367 */ 1368 PJ_DECL(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id); 1369 1370 1371 /** 1372 * Get the conference port identification associated with the call. 1373 * 1374 * @param call_id Call identification. 1375 * 1376 * @return Conference port ID, or PJSUA_INVALID_ID when the 1377 * media has not been established or is not active. 1378 */ 1379 PJ_DECL(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id); 1380 1381 /** 1382 * Obtain detail information about the specified call. 1383 * 1384 * @param call_id Call identification. 1385 * @param info Call info to be initialized. 1386 * 1387 * @return PJ_SUCCESS on success, or the appropriate error code. 1388 */ 1389 PJ_DECL(pj_status_t) pjsua_call_get_info(pjsua_call_id call_id, 533 1390 pjsua_call_info *info); 534 1391 535 1392 536 1393 /** 537 * Duplicate call info. 538 */ 539 PJ_DECL(void) pjsua_call_info_dup(pj_pool_t *pool, 540 pjsua_call_info *dst_info, 541 const pjsua_call_info *src_info); 542 543 544 /** 545 * Make outgoing call. 546 */ 547 PJ_DECL(pj_status_t) pjsua_call_make_call(unsigned acc_id, 548 const pj_str_t *dst_uri, 549 int *p_call_index); 550 551 552 /** 553 * Answer call. 554 */ 555 PJ_DECL(pj_status_t) pjsua_call_answer(int call_index, int code); 556 557 /** 558 * Hangup call. 559 */ 560 PJ_DECL(void) pjsua_call_hangup(int call_index); 561 562 563 /** 564 * Put call on-hold. 565 */ 566 PJ_DECL(pj_status_t) pjsua_call_set_hold(int call_index); 1394 * Attach application specific data to the call. 1395 * 1396 * @param call_id Call identification. 1397 * @param user_data Arbitrary data to be attached to the call. 1398 * 1399 * @return The user data. 1400 */ 1401 PJ_DECL(pj_status_t) pjsua_call_set_user_data(pjsua_call_id call_id, 1402 void *user_data); 1403 1404 1405 /** 1406 * Get user data attached to the call. 1407 * 1408 * @param call_id Call identification. 1409 * 1410 * @return The user data. 1411 */ 1412 PJ_DECL(void*) pjsua_call_get_user_data(pjsua_call_id call_id); 1413 1414 1415 /** 1416 * Send response to incoming INVITE request. 1417 * 1418 * @param call_id Incoming call identification. 1419 * @param code Status code, (100-699). 1420 * @param reason Optional reason phrase. If NULL, default text 1421 * will be used. 1422 * @param msg_data Optional list of headers etc to be added to outgoing 1423 * response message. 1424 * 1425 * @return PJ_SUCCESS on success, or the appropriate error code. 1426 */ 1427 PJ_DECL(pj_status_t) pjsua_call_answer(pjsua_call_id call_id, 1428 unsigned code, 1429 const pj_str_t *reason, 1430 const pjsua_msg_data *msg_data); 1431 1432 /** 1433 * Hangup call by using method that is appropriate according to the 1434 * call state. 1435 * 1436 * @param call_id Call identification. 1437 * @param code Optional status code to be sent when we're rejecting 1438 * incoming call. If the value is zero, "603/Decline" 1439 * will be sent. 1440 * @param reason Optional reason phrase to be sent when we're rejecting 1441 * incoming call. If NULL, default text will be used. 1442 * @param msg_data Optional list of headers etc to be added to outgoing 1443 * request/response message. 1444 * 1445 * @return PJ_SUCCESS on success, or the appropriate error code. 1446 */ 1447 PJ_DECL(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id, 1448 unsigned code, 1449 const pj_str_t *reason, 1450 const pjsua_msg_data *msg_data); 1451 1452 1453 /** 1454 * Put the specified call on hold. 1455 * 1456 * @param call_id Call identification. 1457 * @param msg_data Optional message components to be sent with 1458 * the request. 1459 * 1460 * @return PJ_SUCCESS on success, or the appropriate error code. 1461 */ 1462 PJ_DECL(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id, 1463 const pjsua_msg_data *msg_data); 567 1464 568 1465 569 1466 /** 570 1467 * Send re-INVITE (to release hold). 571 */ 572 PJ_DECL(pj_status_t) pjsua_call_reinvite(int call_index); 573 574 575 /** 576 * Transfer call. 577 */ 578 PJ_DECL(pj_status_t) pjsua_call_xfer(unsigned call_index, const pj_str_t *dest); 579 580 /** 581 * Dial DTMF. 582 */ 583 PJ_DECL(pj_status_t) pjsua_call_dial_dtmf(unsigned call_index, 1468 * 1469 * @param call_id Call identification. 1470 * @param unhold If this argument is non-zero and the call is locally 1471 * held, this will release the local hold. 1472 * @param msg_data Optional message components to be sent with 1473 * the request. 1474 * 1475 * @return PJ_SUCCESS on success, or the appropriate error code. 1476 */ 1477 PJ_DECL(pj_status_t) pjsua_call_reinvite(pjsua_call_id call_id, 1478 pj_bool_t unhold, 1479 const pjsua_msg_data *msg_data); 1480 1481 1482 /** 1483 * Initiate call transfer to the specified address. 1484 * 1485 * @param call_id Call identification. 1486 * @param dest Address of new target to be contacted. 1487 * @param msg_data Optional message components to be sent with 1488 * the request. 1489 * 1490 * @return PJ_SUCCESS on success, or the appropriate error code. 1491 */ 1492 PJ_DECL(pj_status_t) pjsua_call_xfer(pjsua_call_id call_id, 1493 const pj_str_t *dest, 1494 const pjsua_msg_data *msg_data); 1495 1496 /** 1497 * Send DTMF digits to remote using RFC 2833 payload formats. 1498 * 1499 * @param call_id Call identification. 1500 * @param digits DTMF digits to be sent. 1501 * 1502 * @return PJ_SUCCESS on success, or the appropriate error code. 1503 */ 1504 PJ_DECL(pj_status_t) pjsua_call_dial_dtmf(pjsua_call_id call_id, 584 1505 const pj_str_t *digits); 585 1506 586 587 1507 /** 588 1508 * Send instant messaging inside INVITE session. 589 */ 590 PJ_DECL(pj_status_t) pjsua_call_send_im(int call_index, const pj_str_t *text); 1509 * 1510 * @param call_id Call identification. 1511 * @param mime_type Optional MIME type. If NULL, then "text/plain" is 1512 * assumed. 1513 * @param content The message content. 1514 * @param msg_data Optional list of headers etc to be included in outgoing 1515 * request. The body descriptor in the msg_data is 1516 * ignored. 1517 * @param user_data Optional user data, which will be given back when 1518 * the IM callback is called. 1519 * 1520 * @return PJ_SUCCESS on success, or the appropriate error code. 1521 */ 1522 PJ_DECL(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id, 1523 const pj_str_t *mime_type, 1524 const pj_str_t *content, 1525 const pjsua_msg_data *msg_data, 1526 void *user_data); 591 1527 592 1528 593 1529 /** 594 1530 * Send IM typing indication inside INVITE session. 595 */ 596 PJ_DECL(pj_status_t) pjsua_call_send_typing_ind(int call_index, 597 pj_bool_t is_typing); 1531 * 1532 * @param call_id Call identification. 1533 * @param is_typing Non-zero to indicate to remote that local person is 1534 * currently typing an IM. 1535 * @param msg_data Optional list of headers etc to be included in outgoing 1536 * request. 1537 * 1538 * @return PJ_SUCCESS on success, or the appropriate error code. 1539 */ 1540 PJ_DECL(pj_status_t) pjsua_call_send_typing_ind(pjsua_call_id call_id, 1541 pj_bool_t is_typing, 1542 const pjsua_msg_data*msg_data); 598 1543 599 1544 /** … … 605 1550 /** 606 1551 * Dump call and media statistics to string. 607 */ 608 PJ_DECL(void) pjsua_call_dump(int call_index, int with_media, 609 char *buffer, unsigned maxlen, 610 const char *indent); 1552 * 1553 * @param call_id Call identification. 1554 * @param with_media Non-zero to include media information too. 1555 * @param buffer Buffer where the statistics are to be written to. 1556 * @param maxlen Maximum length of buffer. 1557 * @param indent Spaces for left indentation. 1558 * 1559 * @return PJ_SUCCESS on success. 1560 */ 1561 PJ_DECL(pj_status_t) pjsua_call_dump(pjsua_call_id call_id, 1562 pj_bool_t with_media, 1563 char *buffer, 1564 unsigned maxlen, 1565 const char *indent); 611 1566 612 1567 613 1568 /***************************************************************************** 614 * PJSUA Account and Client Registration API (defined in pjsua_reg.c). 615 */ 616 617 618 /** 619 * Get number of accounts. 620 */ 621 PJ_DECL(unsigned) pjsua_get_acc_count(void); 622 623 /** 624 * Get account info. 1569 * PJSUA Account and Client Registration API. 1570 */ 1571 1572 1573 /** 1574 * Get number of current accounts. 1575 * 1576 * @return Current number of accounts. 1577 */ 1578 PJ_DECL(unsigned) pjsua_acc_get_count(void); 1579 1580 1581 /** 1582 * Check if the specified account ID is valid. 1583 * 1584 * @param acc_id Account ID to check. 1585 * 1586 * @return Non-zero if account ID is valid. 1587 */ 1588 PJ_DECL(pj_bool_t) pjsua_acc_is_valid(pjsua_acc_id acc_id); 1589 1590 1591 /** 1592 * Add a new account to pjsua. PJSUA must have been initialized (with 1593 * #pjsua_init()) before calling this function. 1594 * 1595 * @param cfg Account configuration. 1596 * @param is_default If non-zero, this account will be set as the default 1597 * account. The default account will be used when sending 1598 * outgoing requests (e.g. making call) when no account is 1599 * specified, and when receiving incoming requests when the 1600 * request does not match any accounts. It is recommended 1601 * that default account is set to local/LAN account. 1602 * @param p_acc_id Pointer to receive account ID of the new account. 1603 * 1604 * @return PJ_SUCCESS on success, or the appropriate error code. 1605 */ 1606 PJ_DECL(pj_status_t) pjsua_acc_add(const pjsua_acc_config *cfg, 1607 pj_bool_t is_default, 1608 pjsua_acc_id *p_acc_id); 1609 1610 1611 /** 1612 * Add a local account. A local account is used to identify local endpoint 1613 * instead of a specific user, and for this reason, a transport ID is needed 1614 * to obtain the local address information. 1615 * 1616 * @param tid Transport ID to generate account address. 1617 * @param is_default If non-zero, this account will be set as the default 1618 * account. The default account will be used when sending 1619 * outgoing requests (e.g. making call) when no account is 1620 * specified, and when receiving incoming requests when the 1621 * request does not match any accounts. It is recommended 1622 * that default account is set to local/LAN account. 1623 * @param p_acc_id Pointer to receive account ID of the new account. 1624 * 1625 * @return PJ_SUCCESS on success, or the appropriate error code. 1626 */ 1627 PJ_DECL(pj_status_t) pjsua_acc_add_local(pjsua_transport_id tid, 1628 pj_bool_t is_default, 1629 pjsua_acc_id *p_acc_id); 1630 1631 /** 1632 * Delete account. 1633 * 1634 * @param acc_id Id of the account to be deleted. 1635 * 1636 * @return PJ_SUCCESS on success, or the appropriate error code. 1637 */ 1638 PJ_DECL(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_id); 1639 1640 1641 /** 1642 * Modify account information. 1643 * 1644 * @param acc_id Id of the account to be modified. 1645 * @param cfg New account configuration. 1646 * 1647 * @return PJ_SUCCESS on success, or the appropriate error code. 1648 */ 1649 PJ_DECL(pj_status_t) pjsua_acc_modify(pjsua_acc_id acc_id, 1650 const pjsua_acc_config *cfg); 1651 1652 1653 /** 1654 * Modify account's presence status to be advertised to remote/presence 1655 * subscribers. 1656 * 1657 * @param acc_id The account ID. 1658 * @param is_online True of false. 1659 * 1660 * @return PJ_SUCCESS on success, or the appropriate error code. 1661 */ 1662 PJ_DECL(pj_status_t) pjsua_acc_set_online_status(pjsua_acc_id acc_id, 1663 pj_bool_t is_online); 1664 1665 1666 /** 1667 * Update registration or perform unregistration. 1668 * 1669 * @param acc_id The account ID. 1670 * @param renew If renew argument is zero, this will start 1671 * unregistration process. 1672 * 1673 * @return PJ_SUCCESS on success, or the appropriate error code. 1674 */ 1675 PJ_DECL(pj_status_t) pjsua_acc_set_registration(pjsua_acc_id acc_id, 1676 pj_bool_t renew); 1677 1678 1679 /** 1680 * Get account information. 1681 * 1682 * @param acc_id Account identification. 1683 * @param info Pointer to receive account information. 1684 * 1685 * @return PJ_SUCCESS on success, or the appropriate error code. 625 1686 */ 626 1687 PJ_DECL(pj_status_t) pjsua_acc_get_info(pjsua_acc_id acc_id, … … 629 1690 630 1691 /** 631 * Enum accounts id. 632 */ 633 PJ_DECL(pj_status_t) pjsua_acc_enum_id( pjsua_acc_id ids[], 634 unsigned *count ); 1692 * Enum accounts all account ids. 1693 * 1694 * @param ids Array of account IDs to be initialized. 1695 * @param count In input, specifies the maximum number of elements. 1696 * On return, it contains the actual number of elements. 1697 * 1698 * @return PJ_SUCCESS on success, or the appropriate error code. 1699 */ 1700 PJ_DECL(pj_status_t) pjsua_enum_accs(pjsua_acc_id ids[], 1701 unsigned *count ); 635 1702 636 1703 637 1704 /** 638 1705 * Enum accounts info. 1706 * 1707 * @param info Array of account infos to be initialized. 1708 * @param count In input, specifies the maximum number of elements. 1709 * On return, it contains the actual number of elements. 1710 * 1711 * @return PJ_SUCCESS on success, or the appropriate error code. 639 1712 */ 640 1713 PJ_DECL(pj_status_t) pjsua_acc_enum_info( pjsua_acc_info info[], … … 643 1716 644 1717 /** 645 * Find account for outgoing request. 1718 * This is an internal function to find the most appropriate account to 1719 * used to reach to the specified URL. 1720 * 1721 * @param url The remote URL to reach. 1722 * 1723 * @return Account id. 646 1724 */ 647 1725 PJ_DECL(pjsua_acc_id) pjsua_acc_find_for_outgoing(const pj_str_t *url); 648 1726 649 /** 650 * Find account for incoming request. 1727 1728 /** 1729 * This is an internal function to find the most appropriate account to be 1730 * used to handle incoming calls. 1731 * 1732 * @param rdata The incoming request message. 1733 * 1734 * @return Account id. 651 1735 */ 652 1736 PJ_DECL(pjsua_acc_id) pjsua_acc_find_for_incoming(pjsip_rx_data *rdata); 653 654 /**655 * Add a new account.656 * This function should be called after pjsua_init().657 * Application should call pjsua_acc_set_registration() to start658 * registration for this account.659 */660 PJ_DECL(pj_status_t) pjsua_acc_add(const pjsua_acc_config *cfg,661 pjsua_acc_id *acc_id);662 663 /**664 * Delete account.665 */666 PJ_DECL(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_id);667 668 669 /**670 * Set account's presence status.671 */672 PJ_DECL(pj_status_t) pjsua_acc_set_online_status(pjsua_acc_id acc_id,673 pj_bool_t is_online);674 675 676 /**677 * Update registration or perform unregistration. If renew argument is zero,678 * this will start unregistration process.679 */680 PJ_DECL(pj_status_t) pjsua_acc_set_registration(pjsua_acc_id acc_id,681 pj_bool_t renew);682 683 1737 684 1738 … … 689 1743 690 1744 /** 691 * Get buddy count. 1745 * Get total number of buddies. 1746 * 1747 * @return Number of buddies. 692 1748 */ 693 1749 PJ_DECL(unsigned) pjsua_get_buddy_count(void); … … 695 1751 696 1752 /** 697 * Get buddy info. 698 */ 699 PJ_DECL(pj_status_t) pjsua_buddy_get_info(pjsua_buddy_id buddy_index, 1753 * Check if buddy ID is valid. 1754 * 1755 * @param buddy_id Buddy ID to check. 1756 * 1757 * @return Non-zero if buddy ID is valid. 1758 */ 1759 PJ_DECL(pj_bool_t) pjsua_buddy_is_valid(pjsua_buddy_id buddy_id); 1760 1761 1762 /** 1763 * Enum buddy IDs. 1764 * 1765 * @param ids Array of ids to be initialized. 1766 * @param count On input, specifies max elements in the array. 1767 * On return, it contains actual number of elements 1768 * that have been initialized. 1769 * 1770 * @return PJ_SUCCESS on success, or the appropriate error code. 1771 */ 1772 PJ_DECL(pj_status_t) pjsua_enum_buddies(pjsua_buddy_id ids[], 1773 unsigned *count); 1774 1775 /** 1776 * Get detailed buddy info. 1777 * 1778 * @param buddy_id The buddy identification. 1779 * @param info Pointer to receive information about buddy. 1780 * 1781 * @return PJ_SUCCESS on success, or the appropriate error code. 1782 */ 1783 PJ_DECL(pj_status_t) pjsua_buddy_get_info(pjsua_buddy_id buddy_id, 700 1784 pjsua_buddy_info *info); 701 1785 702 1786 /** 703 1787 * Add new buddy. 704 */ 705 PJ_DECL(pj_status_t) pjsua_buddy_add(const pj_str_t *uri, 706 pjsua_buddy_id *buddy_index); 1788 * 1789 * @param cfg Buddy configuration. 1790 * @param p_buddy_id Pointer to receive buddy ID. 1791 * 1792 * @return PJ_SUCCESS on success, or the appropriate error code. 1793 */ 1794 PJ_DECL(pj_status_t) pjsua_buddy_add(const pjsua_buddy_config *cfg, 1795 pjsua_buddy_id *p_buddy_id); 707 1796 708 1797 709 1798 /** 710 1799 * Delete buddy. 711 */ 712 PJ_DECL(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_index); 1800 * 1801 * @param buddy_id Buddy identification. 1802 * 1803 * @return PJ_SUCCESS on success, or the appropriate error code. 1804 */ 1805 PJ_DECL(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id); 713 1806 714 1807 715 1808 /** 716 1809 * Enable/disable buddy's presence monitoring. 717 */ 718 PJ_DECL(pj_status_t) pjsua_buddy_subscribe_pres(pjsua_buddy_id buddy_index, 719 pj_bool_t monitor); 720 721 722 /** 723 * Dump presence subscriptions. 724 */ 725 PJ_DECL(void) pjsua_pres_dump(pj_bool_t detail); 1810 * 1811 * @param buddy_id Buddy identification. 1812 * @param subscribe Specify non-zero to activate presence subscription to 1813 * the specified buddy. 1814 * 1815 * @return PJ_SUCCESS on success, or the appropriate error code. 1816 */ 1817 PJ_DECL(pj_status_t) pjsua_buddy_subscribe_pres(pjsua_buddy_id buddy_id, 1818 pj_bool_t subscribe); 1819 1820 1821 /** 1822 * Dump presence subscriptions to log file. 1823 * 1824 * @param verbose Yes or no. 1825 */ 1826 PJ_DECL(void) pjsua_pres_dump(pj_bool_t verbose); 726 1827 727 1828 … … 738 1839 739 1840 /** 740 * Send IM outside dialog. 741 */ 742 PJ_DECL(pj_status_t) pjsua_im_send(int acc_id, const pj_str_t *dst_uri, 743 const pj_str_t *text); 1841 * Send instant messaging outside dialog, using the specified account for 1842 * route set and authentication. 1843 * 1844 * @param acc_id Account ID to be used to send the request. 1845 * @param to Remote URI. 1846 * @param mime_type Optional MIME type. If NULL, then "text/plain" is 1847 * assumed. 1848 * @param content The message content. 1849 * @param msg_data Optional list of headers etc to be included in outgoing 1850 * request. The body descriptor in the msg_data is 1851 * ignored. 1852 * @param user_data Optional user data, which will be given back when 1853 * the IM callback is called. 1854 * 1855 * @return PJ_SUCCESS on success, or the appropriate error code. 1856 */ 1857 PJ_DECL(pj_status_t) pjsua_im_send(pjsua_acc_id acc_id, 1858 const pj_str_t *to, 1859 const pj_str_t *mime_type, 1860 const pj_str_t *content, 1861 const pjsua_msg_data *msg_data, 1862 void *user_data); 744 1863 745 1864 746 1865 /** 747 1866 * Send typing indication outside dialog. 748 */ 749 PJ_DECL(pj_status_t) pjsua_im_typing(int acc_id, const pj_str_t *dst_uri, 750 pj_bool_t is_typing); 1867 * 1868 * @param acc_id Account ID to be used to send the request. 1869 * @param to Remote URI. 1870 * @param is_typing If non-zero, it tells remote person that local person 1871 * is currently composing an IM. 1872 * @param msg_data Optional list of headers etc to be added to outgoing 1873 * request. 1874 * 1875 * @return PJ_SUCCESS on success, or the appropriate error code. 1876 */ 1877 PJ_DECL(pj_status_t) pjsua_im_typing(pjsua_acc_id acc_id, 1878 const pj_str_t *to, 1879 pj_bool_t is_typing, 1880 const pjsua_msg_data *msg_data); 751 1881 752 1882 753 1883 754 1884 /***************************************************************************** 755 * Media.1885 * Conference bridge manipulation. 756 1886 */ 757 1887 758 1888 /** 759 1889 * Get maxinum number of conference ports. 760 */ 761 PJ_DECL(unsigned) pjsua_conf_max_ports(void); 762 763 764 /** 765 * Enum all conference ports. 766 */ 767 PJ_DECL(pj_status_t) pjsua_conf_enum_port_ids(pjsua_conf_port_id id[], 768 unsigned *count); 1890 * 1891 * @return Maximum number of ports in the conference bridge. 1892 */ 1893 PJ_DECL(unsigned) pjsua_conf_get_max_ports(void); 1894 1895 1896 /** 1897 * Get current number of active ports in the bridge. 1898 * 1899 * @return The number. 1900 */ 1901 PJ_DECL(unsigned) pjsua_conf_get_active_ports(void); 1902 1903 1904 /** 1905 * Enumerate all conference ports. 1906 * 1907 * @param id Array of conference port ID to be initialized. 1908 * @param count On input, specifies max elements in the array. 1909 * On return, it contains actual number of elements 1910 * that have been initialized. 1911 * 1912 * @return PJ_SUCCESS on success, or the appropriate error code. 1913 */ 1914 PJ_DECL(pj_status_t) pjsua_enum_conf_ports(pjsua_conf_port_id id[], 1915 unsigned *count); 769 1916 770 1917 771 1918 /** 772 1919 * Get information about the specified conference port 1920 * 1921 * @param id Port identification. 1922 * @param info Pointer to store the port info. 1923 * 1924 * @return PJ_SUCCESS on success, or the appropriate error code. 773 1925 */ 774 1926 PJ_DECL(pj_status_t) pjsua_conf_get_port_info( pjsua_conf_port_id id, … … 777 1929 778 1930 /** 779 * Connect conference port. 780 */ 781 PJ_DECL(pj_status_t) pjsua_conf_connect(pjsua_conf_port_id src_port, 782 pjsua_conf_port_id dst_port); 783 784 785 /** 786 * Connect conference port connection. 787 */ 788 PJ_DECL(pj_status_t) pjsua_conf_disconnect(pjsua_conf_port_id src_port, 789 pjsua_conf_port_id dst_port); 790 791 792 /** 793 * Create a file player. 1931 * Establish unidirectional media flow from souce to sink. One source 1932 * may transmit to multiple destinations/sink. And if multiple 1933 * sources are transmitting to the same sink, the media will be mixed 1934 * together. Source and sink may refer to the same ID, effectively 1935 * looping the media. 1936 * 1937 * If bidirectional media flow is desired, application needs to call 1938 * this function twice, with the second one having the arguments 1939 * reversed. 1940 * 1941 * @param source Port ID of the source media/transmitter. 1942 * @param sink Port ID of the destination media/received. 1943 * 1944 * @return PJ_SUCCESS on success, or the appropriate error code. 1945 */ 1946 PJ_DECL(pj_status_t) pjsua_conf_connect(pjsua_conf_port_id source, 1947 pjsua_conf_port_id sink); 1948 1949 1950 /** 1951 * Disconnect media flow from the source to destination port. 1952 * 1953 * @param source Port ID of the source media/transmitter. 1954 * @param sink Port ID of the destination media/received. 1955 * 1956 * @return PJ_SUCCESS on success, or the appropriate error code. 1957 */ 1958 PJ_DECL(pj_status_t) pjsua_conf_disconnect(pjsua_conf_port_id source, 1959 pjsua_conf_port_id sink); 1960 1961 1962 /***************************************************************************** 1963 * File player. 1964 */ 1965 1966 /** 1967 * Create a file player, and automatically connect this player to 1968 * the conference bridge. 1969 * 1970 * @param filename The filename to be played. Currently only 1971 * WAV files are supported. 1972 * @param options Options (currently zero). 1973 * @param user_data Arbitrary user data to be associated with the player. 1974 * @param p_id Pointer to receive player ID. 1975 * 1976 * @return PJ_SUCCESS on success, or the appropriate error code. 794 1977 */ 795 1978 PJ_DECL(pj_status_t) pjsua_player_create(const pj_str_t *filename, 796 pjsua_player_id *id); 797 798 799 /** 800 * Get conference port associated with player. 1979 unsigned options, 1980 void *user_data, 1981 pjsua_player_id *p_id); 1982 1983 1984 /** 1985 * Get conference port ID associated with player. 1986 * 1987 * @param id The file player ID. 1988 * 1989 * @return Conference port ID associated with this player. 801 1990 */ 802 1991 PJ_DECL(pjsua_conf_port_id) pjsua_player_get_conf_port(pjsua_player_id id); … … 805 1994 /** 806 1995 * Set playback position. 1996 * 1997 * @param id The file player ID. 1998 * @param samples The playback position, in samples. Application can 1999 * specify zero to re-start the playback. 2000 * 2001 * @return PJ_SUCCESS on success, or the appropriate error code. 807 2002 */ 808 2003 PJ_DECL(pj_status_t) pjsua_player_set_pos(pjsua_player_id id, … … 811 2006 812 2007 /** 813 * Destroy player. 2008 * Close the file, remove the player from the bridge, and free 2009 * resources associated with the file player. 2010 * 2011 * @param id The file player ID. 2012 * 2013 * @return PJ_SUCCESS on success, or the appropriate error code. 814 2014 */ 815 2015 PJ_DECL(pj_status_t) pjsua_player_destroy(pjsua_player_id id); 816 2016 817 2017 818 819 /** 820 * Create a file recorder. 2018 /***************************************************************************** 2019 * File recorder. 2020 */ 2021 2022 /** 2023 * Create a file recorder, and automatically connect this recorder to 2024 * the conference bridge. 2025 * 2026 * @param filename Output file name. 2027 * @param file_format Specify the file format (currently only WAV is 2028 * supported, so the value MUST be zero). 2029 * @param encoding Specify the encoding to be applied to the file. 2030 * Currently only 16bit raw PCM is supported, so 2031 * the value must be NULL. 2032 * @param max_size Maximum file size. Specify -1 to remove size 2033 * limitation. 2034 * @param options Optional options. 2035 * @param user_data Arbitrary user data which will be given in the 2036 * callback once the recording complete. 2037 * @param p_id Pointer to receive the recorder instance. 2038 * 2039 * @return PJ_SUCCESS on success, or the appropriate error code. 821 2040 */ 822 2041 PJ_DECL(pj_status_t) pjsua_recorder_create(const pj_str_t *filename, 823 pjsua_recorder_id *id); 2042 unsigned file_format, 2043 const pj_str_t *encoding, 2044 pj_ssize_t max_size, 2045 unsigned options, 2046 void *user_data, 2047 pjsua_recorder_id *p_id); 824 2048 825 2049 826 2050 /** 827 2051 * Get conference port associated with recorder. 2052 * 2053 * @param id The recorder ID. 2054 * 2055 * @return Conference port ID associated with this recorder. 828 2056 */ 829 2057 PJ_DECL(pjsua_conf_port_id) pjsua_recorder_get_conf_port(pjsua_recorder_id id); … … 831 2059 832 2060 /** 833 * Destroy recorder (will complete recording). 2061 * Destroy recorder (this will complete recording). 2062 * 2063 * @param id The recorder ID. 2064 * 2065 * @return PJ_SUCCESS on success, or the appropriate error code. 834 2066 */ 835 2067 PJ_DECL(pj_status_t) pjsua_recorder_destroy(pjsua_recorder_id id); 836 2068 837 2069 2070 /***************************************************************************** 2071 * Sound devices. 2072 */ 2073 838 2074 /** 839 2075 * Enum sound devices. 840 */ 841 PJ_DECL(pj_status_t) pjsua_enum_snd_devices(unsigned *count, 842 pjmedia_snd_dev_info info[]); 843 844 845 /** 846 * Select or change sound device. 847 * This will only change the device ID in configuration (not changing 848 * the current device). 849 */ 850 PJ_DECL(pj_status_t) pjsua_set_snd_dev(int snd_capture_id, 851 int snd_player_id); 2076 * 2077 * @param info Array of info to be initialized. 2078 * @param count On input, specifies max elements in the array. 2079 * On return, it contains actual number of elements 2080 * that have been initialized. 2081 * 2082 * @return PJ_SUCCESS on success, or the appropriate error code. 2083 */ 2084 PJ_DECL(pj_status_t) pjsua_enum_snd_devs(pjmedia_snd_dev_info info[], 2085 unsigned *count); 2086 2087 2088 /** 2089 * Select or change sound device. Application may call this function at 2090 * any time to replace current sound device. 2091 * 2092 * @param capture_dev Device ID of the capture device. 2093 * @param playback_dev Device ID of the playback device. 2094 * 2095 * @return PJ_SUCCESS on success, or the appropriate error code. 2096 */ 2097 PJ_DECL(pj_status_t) pjsua_set_snd_dev(int capture_dev, 2098 int playback_dev); 2099 2100 2101 /** 2102 * Set pjsua to use null sound device. The null sound device only provides 2103 * the timing needed by the conference bridge, and will not interract with 2104 * any hardware. 2105 * 2106 * @return PJ_SUCCESS on success, or the appropriate error code. 2107 */ 2108 PJ_DECL(pj_status_t) pjsua_set_null_snd_dev(void); 2109 2110 2111 /***************************************************************************** 2112 * Codecs. 2113 */ 2114 2115 /** 2116 * Enum all supported codecs in the system. 2117 * 2118 * @param id Array of ID to be initialized. 2119 * @param count On input, specifies max elements in the array. 2120 * On return, it contains actual number of elements 2121 * that have been initialized. 2122 * 2123 * @return PJ_SUCCESS on success, or the appropriate error code. 2124 */ 2125 PJ_DECL(pj_status_t) pjsua_enum_codecs( pjsua_codec_info id[], 2126 unsigned *count ); 2127 2128 2129 /** 2130 * Change codec priority. 2131 * 2132 * @param id Codec ID. 2133 * @param priority Codec priority, 0-255, where zero means to disable 2134 * the codec. 2135 * 2136 * @return PJ_SUCCESS on success, or the appropriate error code. 2137 */ 2138 PJ_DECL(pj_status_t) pjsua_codec_set_priority( const pj_str_t *id, 2139 pj_uint8_t priority ); 2140 2141 2142 /** 2143 * Get codec parameters. 2144 * 2145 * @param id Codec ID. 2146 * @param param Structure to receive codec parameters. 2147 * 2148 * @return PJ_SUCCESS on success, or the appropriate error code. 2149 */ 2150 PJ_DECL(pj_status_t) pjsua_codec_get_param( const pj_str_t *id, 2151 pjmedia_codec_param *param ); 2152 2153 2154 /** 2155 * Set codec parameters. 2156 * 2157 * @param id Codec ID. 2158 * @param param Codec parameter to set. 2159 * 2160 * @return PJ_SUCCESS on success, or the appropriate error code. 2161 */ 2162 PJ_DECL(pj_status_t) pjsua_codec_set_param( const pj_str_t *id, 2163 const pjmedia_codec_param *param); 2164 2165 2166 852 2167 853 2168 … … 857 2172 */ 858 2173 859 /** String to describe invite session states */860 extern const char *pjsua_inv_state_names[];861 862 /**863 * Parse arguments (pjsua_opt.c).864 */865 PJ_DECL(pj_status_t) pjsua_parse_args(int argc, char *argv[],866 pjsua_config *cfg,867 pj_str_t *uri_to_call);868 869 /**870 * Load settings from a file.871 */872 PJ_DECL(pj_status_t) pjsua_load_settings(const char *filename,873 pjsua_config *cfg,874 pj_str_t *uri_to_call);875 876 /**877 * Get pjsua running config.878 */879 PJ_DECL(void) pjsua_get_config(pj_pool_t *pool,880 pjsua_config *config);881 882 883 /**884 * Dump settings.885 * If cfg is NULL, it will dump current settings.886 */887 PJ_DECL(int) pjsua_dump_settings(const pjsua_config *cfg,888 char *buf, pj_size_t max);889 890 /**891 * Save settings to a file.892 */893 PJ_DECL(pj_status_t) pjsua_save_settings(const char *filename,894 const pjsua_config *cfg);895 896 897 2174 /* 898 2175 * Verify that valid SIP url is given. 899 * @return PJ_SUCCESS if valid. 2176 * 2177 * @param c_url The URL, as NULL terminated string. 2178 * 2179 * @return PJ_SUCCESS on success, or the appropriate error code. 900 2180 */ 901 2181 PJ_DECL(pj_status_t) pjsua_verify_sip_url(const char *c_url); 902 2182 903 /*904 * Dump application states.905 */906 PJ_DECL(void) pjsua_dump(pj_bool_t detail);907 2183 908 2184 /** 909 2185 * Display error message for the specified error code. 2186 * 2187 * @param sender The log sender field. 2188 * @param title Message title for the error. 2189 * @param status Status code. 910 2190 */ 911 2191 PJ_DECL(void) pjsua_perror(const char *sender, const char *title, -
pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c
r492 r503 18 18 */ 19 19 #include <pjsua-lib/pjsua.h> 20 #include <pj/log.h> 21 #include "pjsua_imp.h" 22 23 /* 24 * pjsua_call.c 25 * 26 * Call (INVITE) related stuffs. 27 */ 28 29 #define THIS_FILE "pjsua_call.c" 30 31 32 #define REFRESH_CALL_TIMER 0x63 33 #define HANGUP_CALL_TIMER 0x64 34 35 /* Proto */ 36 static void schedule_call_timer( pjsua_call *call, pj_timer_entry *e, 37 int timer_type, int duration ); 38 39 /* 40 * Timer callback when UAS needs to send re-INVITE to see if remote 41 * is still there. 42 */ 43 static void call_on_timer(pj_timer_heap_t *ht, pj_timer_entry *e) 44 { 45 pjsua_call *call = e->user_data; 46 47 PJ_UNUSED_ARG(ht); 48 49 if (e->id == REFRESH_CALL_TIMER) { 50 51 /* If call is still not connected, hangup. */ 52 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) { 53 PJ_LOG(3,(THIS_FILE, "Refresh call timer is called when " 54 "invite is still not confirmed. Call %d will " 55 "disconnect.", call->index)); 56 pjsua_call_hangup(call->index); 57 } else { 58 PJ_LOG(3,(THIS_FILE, "Refreshing call %d", call->index)); 59 schedule_call_timer(call,e,REFRESH_CALL_TIMER, 60 pjsua.config.uas_refresh); 61 pjsua_call_reinvite(call->index); 62 } 63 64 } else if (e->id == HANGUP_CALL_TIMER) { 65 PJ_LOG(3,(THIS_FILE, "Call %d duration exceeded, disconnecting call", 66 call->index)); 67 pjsua_call_hangup(call->index); 68 69 } 70 } 71 72 /* 73 * Schedule call timer. 74 */ 75 static void schedule_call_timer( pjsua_call *call, pj_timer_entry *e, 76 int timer_type, int duration ) 77 { 78 pj_time_val timeout; 79 80 if (duration == 0) { 81 /* Cancel timer. */ 82 if (e->id != 0) { 83 pjsip_endpt_cancel_timer(pjsua.endpt, e); 84 e->id = 0; 85 } 86 87 } else { 88 /* Schedule timer. */ 89 timeout.sec = duration; 90 timeout.msec = 0; 91 92 e->cb = &call_on_timer; 93 e->id = timer_type; 94 e->user_data = call; 95 96 pjsip_endpt_schedule_timer( pjsua.endpt, e, &timeout); 97 } 98 } 99 100 101 /* 102 * Destroy the call's media 103 */ 104 static pj_status_t call_destroy_media(int call_index) 105 { 106 pjsua_call *call = &pjsua.calls[call_index]; 107 108 if (call->conf_slot > 0) { 109 pjmedia_conf_remove_port(pjsua.mconf, call->conf_slot); 110 call->conf_slot = 0; 111 } 112 113 if (call->session) { 114 /* Destroy session (this will also close RTP/RTCP sockets). */ 115 pjmedia_session_destroy(call->session); 116 call->session = NULL; 117 118 PJ_LOG(3,(THIS_FILE, "Media session for call %d is destroyed", 119 call_index)); 120 121 } 122 20 #include <pjsua-lib/pjsua_internal.h> 21 22 23 #define THIS_FILE "pjsua_call.c" 24 25 26 /* This callback receives notification from invite session when the 27 * session state has changed. 28 */ 29 static void pjsua_call_on_state_changed(pjsip_inv_session *inv, 30 pjsip_event *e); 31 32 /* This callback is called by invite session framework when UAC session 33 * has forked. 34 */ 35 static void pjsua_call_on_forked( pjsip_inv_session *inv, 36 pjsip_event *e); 37 38 /* 39 * Callback to be called when SDP offer/answer negotiation has just completed 40 * in the session. This function will start/update media if negotiation 41 * has succeeded. 42 */ 43 static void pjsua_call_on_media_update(pjsip_inv_session *inv, 44 pj_status_t status); 45 46 /* 47 * Called when session received new offer. 48 */ 49 static void pjsua_call_on_rx_offer(pjsip_inv_session *inv, 50 const pjmedia_sdp_session *offer); 51 52 /* 53 * This callback is called when transaction state has changed in INVITE 54 * session. We use this to trap: 55 * - incoming REFER request. 56 * - incoming MESSAGE request. 57 */ 58 static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv, 59 pjsip_transaction *tsx, 60 pjsip_event *e); 61 62 63 /* Destroy the call's media */ 64 static pj_status_t call_destroy_media(int call_id); 65 66 /* Create inactive SDP for call hold. */ 67 static pj_status_t create_inactive_sdp(pjsua_call *call, 68 pjmedia_sdp_session **p_answer); 69 70 71 /* 72 * Reset call descriptor. 73 */ 74 static void reset_call(pjsua_call_id id) 75 { 76 pjsua_call *call = &pjsua_var.calls[id]; 77 78 call->index = id; 79 call->inv = NULL; 80 call->user_data = NULL; 81 call->session = NULL; 82 call->xfer_sub = NULL; 83 call->last_code = 0; 84 call->conf_slot = PJSUA_INVALID_ID; 85 call->last_text.ptr = call->last_text_buf_; 86 call->last_text.slen = 0; 87 } 88 89 90 /* 91 * Init call subsystem. 92 */ 93 pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg) 94 { 95 pjsip_inv_callback inv_cb; 96 unsigned i; 97 pj_status_t status; 98 99 /* Init calls array. */ 100 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i) 101 reset_call(i); 102 103 /* Copy config */ 104 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg); 105 106 /* Initialize invite session callback. */ 107 pj_memset(&inv_cb, 0, sizeof(inv_cb)); 108 inv_cb.on_state_changed = &pjsua_call_on_state_changed; 109 inv_cb.on_new_session = &pjsua_call_on_forked; 110 inv_cb.on_media_update = &pjsua_call_on_media_update; 111 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer; 112 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed; 113 114 115 /* Initialize invite session module: */ 116 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb); 117 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 118 119 return status; 120 } 121 122 123 /* 124 * Start call subsystem. 125 */ 126 pj_status_t pjsua_call_subsys_start(void) 127 { 128 /* Nothing to do */ 123 129 return PJ_SUCCESS; 124 130 } 125 131 126 132 127 /* *133 /* 128 134 * Get maximum number of calls configured in pjsua. 129 135 */ 130 136 PJ_DEF(unsigned) pjsua_call_get_max_count(void) 131 137 { 132 return pjsua .config.max_calls;133 } 134 135 136 /* *137 * Get current number ofactive calls.138 return pjsua_var.ua_cfg.max_calls; 139 } 140 141 142 /* 143 * Get number of currently active calls. 138 144 */ 139 145 PJ_DEF(unsigned) pjsua_call_get_count(void) 140 146 { 141 return pjsua.call_cnt; 142 } 143 144 145 /** 146 * Check if the specified call is active. 147 */ 148 PJ_DEF(pj_bool_t) pjsua_call_is_active(unsigned call_index) 149 { 150 PJ_ASSERT_RETURN(call_index < pjsua.config.max_calls, 151 PJ_EINVAL); 152 return pjsua.calls[call_index].inv != NULL && 153 pjsua.calls[call_index].inv->state != PJSIP_INV_STATE_DISCONNECTED; 154 } 155 156 /** 157 * Check if call has a media session. 158 */ 159 PJ_DEF(pj_bool_t) pjsua_call_has_media(unsigned call_index) 160 { 161 PJ_ASSERT_RETURN(call_index < pjsua.config.max_calls, PJ_EINVAL); 162 return pjsua.calls[call_index].session != NULL; 163 } 164 165 166 /** 167 * Get call info. 168 */ 169 PJ_DEF(pj_status_t) pjsua_call_get_info( unsigned call_index, 170 pjsua_call_info *info) 171 { 172 pjsua_call *call; 173 174 PJ_ASSERT_RETURN(call_index < pjsua.config.max_calls, 175 PJ_EINVAL); 176 177 pj_memset(info, 0, sizeof(pjsua_call_info)); 178 179 call = &pjsua.calls[call_index]; 180 info->active = pjsua_call_is_active(call_index); 181 182 if (call->inv == NULL) 183 return PJ_SUCCESS; 184 185 info->index = call_index; 186 info->role = call->inv->role; 187 info->local_info = call->inv->dlg->local.info_str; 188 info->remote_info = call->inv->dlg->remote.info_str; 189 info->state = call->inv->state; 190 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state)); 191 192 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) { 193 194 info->total_duration = call->dis_time; 195 PJ_TIME_VAL_SUB(info->total_duration, call->start_time); 196 197 if (call->conn_time.sec) { 198 info->connect_duration = call->dis_time; 199 PJ_TIME_VAL_SUB(info->total_duration, call->conn_time); 200 } 201 202 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) { 203 204 pj_gettimeofday(&info->total_duration); 205 PJ_TIME_VAL_SUB(info->total_duration, call->start_time); 206 207 pj_gettimeofday(&info->connect_duration); 208 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time); 209 210 } else { 211 pj_gettimeofday(&info->total_duration); 212 PJ_TIME_VAL_SUB(info->total_duration, call->start_time); 213 } 214 215 info->last_status = call->last_code; 216 info->last_status_text = *pjsip_get_status_text(info->last_status); 217 218 info->has_media = (call->session != NULL); 219 info->conf_slot = call->conf_slot; 147 return pjsua_var.call_cnt; 148 } 149 150 151 /* 152 * Enum calls. 153 */ 154 PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[], 155 unsigned *count) 156 { 157 unsigned i, c; 158 159 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL); 160 161 PJSUA_LOCK(); 162 163 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) { 164 if (!pjsua_var.calls[i].inv) 165 continue; 166 ids[c] = i; 167 ++c; 168 } 169 170 *count = c; 171 172 PJSUA_UNLOCK(); 220 173 221 174 return PJ_SUCCESS; … … 223 176 224 177 225 /** 226 * Duplicate call info. 227 */ 228 PJ_DEF(void) pjsua_call_info_dup( pj_pool_t *pool, 229 pjsua_call_info *dst_info, 230 const pjsua_call_info *src_info) 231 { 232 PJ_ASSERT_ON_FAIL(pool && dst_info && src_info, return); 233 234 pj_memcpy(dst_info, src_info, sizeof(pjsua_call_info)); 235 236 pj_strdup(pool, &dst_info->local_info, &src_info->local_info); 237 pj_strdup(pool, &dst_info->remote_info, &src_info->remote_info); 238 239 /* state_text and cause_text belong to pjsip, so don't need to be 240 * duplicated because they'll always be available. 241 */ 242 } 243 244 245 /** 246 * Make outgoing call. 247 */ 248 PJ_DEF(pj_status_t) pjsua_call_make_call(unsigned acc_index, 249 const pj_str_t *dest_uri, 250 int *p_call_index) 178 /* 179 * Make outgoing call to the specified URI using the specified account. 180 */ 181 PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id, 182 const pj_str_t *dest_uri, 183 unsigned options, 184 void *user_data, 185 const pjsua_msg_data *msg_data, 186 pjsua_call_id *p_call_id) 251 187 { 252 188 pjsip_dialog *dlg = NULL; 253 189 pjmedia_sdp_session *offer; 254 190 pjsip_inv_session *inv = NULL; 255 unsigned call_index; 191 pjsua_acc *acc; 192 pjsua_call *call; 193 unsigned call_id; 256 194 pjsip_tx_data *tdata; 257 195 pj_status_t status; 258 196 259 197 260 PJ_ASSERT_RETURN(acc_index==0 || acc_index < pjsua.config.acc_cnt, 198 /* Check that account is valid */ 199 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<PJ_ARRAY_SIZE(pjsua_var.acc), 261 200 PJ_EINVAL); 262 201 202 /* Options must be zero for now */ 203 PJ_ASSERT_RETURN(options == 0, PJ_EINVAL); 204 205 PJSUA_LOCK(); 206 207 acc = &pjsua_var.acc[acc_id]; 208 if (!acc->valid) { 209 pjsua_perror(THIS_FILE, "Unable to make call because account " 210 "is not valid", PJ_EINVALIDOP); 211 PJSUA_UNLOCK(); 212 return PJ_EINVALIDOP; 213 } 263 214 264 215 /* Find free call slot. */ 265 for (call_i ndex=0; call_index<pjsua.config.max_calls; ++call_index) {266 if (pjsua .calls[call_index].inv == NULL)216 for (call_id=0; call_id<pjsua_var.ua_cfg.max_calls; ++call_id) { 217 if (pjsua_var.calls[call_id].inv == NULL) 267 218 break; 268 219 } 269 220 270 if (call_index == pjsua.config.max_calls) { 271 PJ_LOG(3,(THIS_FILE, "Error: too many calls!")); 221 if (call_id == pjsua_var.ua_cfg.max_calls) { 222 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY); 223 PJSUA_UNLOCK(); 272 224 return PJ_ETOOMANY; 273 225 } 274 226 227 call = &pjsua_var.calls[call_id]; 228 275 229 /* Mark call start time. */ 276 pj_gettimeofday(& pjsua.calls[call_index].start_time);230 pj_gettimeofday(&call->start_time); 277 231 278 232 /* Reset first response time */ 279 pjsua.calls[call_index].res_time.sec = 0;233 call->res_time.sec = 0; 280 234 281 235 /* Create outgoing dialog: */ 282 236 status = pjsip_dlg_create_uac( pjsip_ua_instance(), 283 &pjsua.config.acc_config[acc_index].id, 284 &pjsua.config.acc_config[acc_index].contact, 285 dest_uri, dest_uri, 286 &dlg); 237 &acc->cfg.id, &acc->cfg.contact, 238 dest_uri, dest_uri, &dlg); 287 239 if (status != PJ_SUCCESS) { 288 240 pjsua_perror(THIS_FILE, "Dialog creation failed", status); 241 PJSUA_UNLOCK(); 289 242 return status; 290 243 } … … 292 245 /* Get media capability from media endpoint: */ 293 246 294 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, dlg->pool, 1, 295 &pjsua.calls[call_index].skinfo, 296 &offer); 247 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, dlg->pool, 1, 248 &call->skinfo, &offer); 297 249 if (status != PJ_SUCCESS) { 298 250 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status); … … 311 263 /* Create and associate our data in the session. */ 312 264 313 pjsua.calls[call_index].inv = inv; 314 315 dlg->mod_data[pjsua.mod.id] = &pjsua.calls[call_index]; 316 inv->mod_data[pjsua.mod.id] = &pjsua.calls[call_index]; 317 265 call->inv = inv; 266 267 dlg->mod_data[pjsua_var.mod.id] = call; 268 inv->mod_data[pjsua_var.mod.id] = call; 269 270 /* Attach user data */ 271 call->user_data = user_data; 318 272 319 273 /* Set dialog Route-Set: */ 320 321 if (!pj_list_empty(&pjsua.acc[acc_index].route_set)) 322 pjsip_dlg_set_route_set(dlg, &pjsua.acc[acc_index].route_set); 274 if (!pj_list_empty(&acc->route_set)) 275 pjsip_dlg_set_route_set(dlg, &acc->route_set); 323 276 324 277 325 278 /* Set credentials: */ 326 if (pjsua.config.acc_config[acc_index].cred_count) { 327 pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[acc_index]; 279 if (acc->cred_cnt) { 328 280 pjsip_auth_clt_set_credentials( &dlg->auth_sess, 329 acc_cfg->cred_count, 330 acc_cfg->cred_info); 281 acc->cred_cnt, acc->cred); 331 282 } 332 283 … … 341 292 } 342 293 294 295 /* Add additional headers etc */ 296 297 pjsua_process_msg_data( tdata, msg_data); 343 298 344 299 /* Send initial INVITE: */ … … 357 312 } 358 313 359 360 314 /* Done. */ 361 315 362 ++pjsua.call_cnt; 363 364 if (p_call_index) 365 *p_call_index = call_index; 316 ++pjsua_var.call_cnt; 317 318 if (p_call_id) 319 *p_call_id = call_id; 320 321 PJSUA_UNLOCK(); 366 322 367 323 return PJ_SUCCESS; … … 375 331 } 376 332 377 if (call_index != -1) { 378 pjsua.calls[call_index].inv = NULL; 379 } 380 return status; 381 } 382 383 384 /** 385 * Answer call. 386 */ 387 PJ_DEF(pj_status_t) pjsua_call_answer(int call_index, int code) 388 { 389 pjsip_tx_data *tdata; 390 pj_status_t status; 391 392 PJ_ASSERT_RETURN( call_index >= 0 && 393 call_index < (int)pjsua.config.max_calls, 394 PJ_EINVAL); 395 396 if (pjsua.calls[call_index].inv == NULL) { 397 PJ_LOG(3,(THIS_FILE, "Call %d already disconnected")); 398 return PJSIP_ESESSIONTERMINATED; 399 } 400 401 status = pjsip_inv_answer(pjsua.calls[call_index].inv, 402 code, NULL, NULL, &tdata); 403 if (status == PJ_SUCCESS) 404 status = pjsip_inv_send_msg(pjsua.calls[call_index].inv, 405 tdata); 406 407 if (status != PJ_SUCCESS) 408 pjsua_perror(THIS_FILE, "Unable to create/send response", 409 status); 410 333 if (call_id != -1) { 334 reset_call(call_id); 335 } 336 337 PJSUA_UNLOCK(); 411 338 return status; 412 339 } … … 415 342 /** 416 343 * Handle incoming INVITE request. 344 * Called by pjsua_core.c 417 345 */ 418 346 pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) … … 424 352 unsigned options = 0; 425 353 pjsip_inv_session *inv = NULL; 426 int acc_index; 427 unsigned call_index; 354 int acc_id; 355 pjsua_call *call; 356 int call_id = -1; 428 357 pjmedia_sdp_session *answer; 429 358 pj_status_t status; … … 442 371 /* Verify that we can handle the request. */ 443 372 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL, 444 pjsua .endpt, &response);373 pjsua_var.endpt, &response); 445 374 if (status != PJ_SUCCESS) { 446 375 … … 453 382 454 383 pjsip_get_response_addr(response->pool, rdata, &res_addr); 455 pjsip_endpt_send_response(pjsua .endpt, &res_addr, response,384 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response, 456 385 NULL, NULL); 457 386 … … 459 388 460 389 /* Respond with 500 (Internal Server Error) */ 461 pjsip_endpt_respond_stateless(pjsua .endpt, rdata, 500, NULL,390 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, 462 391 NULL, NULL); 463 392 } … … 472 401 473 402 /* Find free call slot. */ 474 for (call_i ndex=0; call_index < pjsua.config.max_calls; ++call_index) {475 if (pjsua .calls[call_index].inv == NULL)403 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) { 404 if (pjsua_var.calls[call_id].inv == NULL) 476 405 break; 477 406 } 478 407 479 if (call_i ndex == PJSUA_MAX_CALLS) {480 pjsip_endpt_respond_stateless(pjsua .endpt, rdata,408 if (call_id == (int)pjsua_var.ua_cfg.max_calls) { 409 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 481 410 PJSIP_SC_BUSY_HERE, NULL, 482 411 NULL, NULL); … … 484 413 } 485 414 415 /* Clear call descriptor */ 416 reset_call(call_id); 417 418 call = &pjsua_var.calls[call_id]; 419 486 420 /* Mark call start time. */ 487 pj_gettimeofday(&pjsua.calls[call_index].start_time); 488 489 /* Reset first response time */ 490 pjsua.calls[call_index].res_time.sec = 0; 421 pj_gettimeofday(&call->start_time); 491 422 492 423 /* Get media capability from media endpoint: */ 493 494 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, rdata->tp_info.pool, 1, 495 &pjsua.calls[call_index].skinfo, 496 &answer ); 497 if (status != PJ_SUCCESS) { 498 pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL, 424 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, 425 rdata->tp_info.pool, 1, 426 &call->skinfo, &answer ); 427 if (status != PJ_SUCCESS) { 428 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, 499 429 NULL, NULL); 500 501 430 return PJ_TRUE; 502 431 } 503 432 504 /* TODO: 505 * 433 /* 506 434 * Get which account is most likely to be associated with this incoming 507 435 * call. We need the account to find which contact URI to put for 508 436 * the call. 509 437 */ 510 acc_i ndex = 0;438 acc_id = pjsua_acc_find_for_incoming(rdata); 511 439 512 440 /* Create dialog: */ 513 514 441 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, 515 &pjsua .config.acc_config[acc_index].contact,442 &pjsua_var.acc[acc_id].cfg.contact, 516 443 &dlg); 517 444 if (status != PJ_SUCCESS) { 518 pjsip_endpt_respond_stateless(pjsua .endpt, rdata, 500, NULL,445 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, 519 446 NULL, NULL); 520 447 … … 522 449 } 523 450 451 /* Set credentials */ 452 if (pjsua_var.acc[acc_id].cred_cnt) { 453 pjsip_auth_clt_set_credentials(&dlg->auth_sess, 454 pjsua_var.acc[acc_id].cred_cnt, 455 pjsua_var.acc[acc_id].cred); 456 } 524 457 525 458 /* Create invite session: */ 526 527 459 status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv); 528 460 if (status != PJ_SUCCESS) { 529 530 461 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL); 531 462 pjsip_dlg_terminate(dlg); … … 534 465 535 466 536 /* Create and attach pjsua data to the dialog: */ 537 538 pjsua.calls[call_index].inv = inv; 539 540 dlg->mod_data[pjsua.mod.id] = &pjsua.calls[call_index]; 541 inv->mod_data[pjsua.mod.id] = &pjsua.calls[call_index]; 467 /* Create and attach pjsua_var data to the dialog: */ 468 call->inv = inv; 469 470 dlg->mod_data[pjsua_var.mod.id] = call; 471 inv->mod_data[pjsua_var.mod.id] = call; 542 472 543 473 … … 545 475 * If auto-answer flag is set, send 200 straight away, otherwise send 100. 546 476 */ 547 548 477 status = pjsip_inv_initial_answer(inv, rdata, 549 (pjsua.config.auto_answer ? 550 pjsua.config.auto_answer : 100), 551 NULL, NULL, &response); 552 if (status != PJ_SUCCESS) { 553 554 int st_code; 555 478 100, NULL, NULL, &response); 479 if (status != PJ_SUCCESS) { 556 480 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE", 557 481 status); 558 482 559 /* If failed to send 2xx response, there's a good chance that it is 560 * because SDP negotiation has failed. 561 */ 562 if (pjsua.config.auto_answer/100 == 2) 563 st_code = PJSIP_SC_UNSUPPORTED_MEDIA_TYPE; 564 else 565 st_code = 500; 566 567 pjsip_dlg_respond(dlg, rdata, st_code, NULL, NULL, NULL); 568 pjsip_inv_terminate(inv, st_code, PJ_FALSE); 483 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL); 484 pjsip_inv_terminate(inv, 500, PJ_FALSE); 569 485 return PJ_TRUE; 570 486 … … 575 491 } 576 492 577 if (pjsua.config.auto_answer < 200) { 578 PJ_LOG(3,(THIS_FILE, 579 "\nIncoming call!!\n" 580 "From: %.*s\n" 581 "To: %.*s\n" 582 "(press 'a' to answer, 'h' to decline)", 583 (int)dlg->remote.info_str.slen, 584 dlg->remote.info_str.ptr, 585 (int)dlg->local.info_str.slen, 586 dlg->local.info_str.ptr)); 587 } else { 588 PJ_LOG(3,(THIS_FILE, 589 "Call From:%.*s To:%.*s was answered with %d (%s)", 590 (int)dlg->remote.info_str.slen, 591 dlg->remote.info_str.ptr, 592 (int)dlg->local.info_str.slen, 593 dlg->local.info_str.ptr, 594 pjsua.config.auto_answer, 595 pjsip_get_status_text(pjsua.config.auto_answer)->ptr )); 596 } 597 598 ++pjsua.call_cnt; 599 600 /* Schedule timer to refresh. */ 601 if (pjsua.config.uas_refresh > 0) { 602 schedule_call_timer( &pjsua.calls[call_index], 603 &pjsua.calls[call_index].refresh_tm, 604 REFRESH_CALL_TIMER, 605 pjsua.config.uas_refresh); 606 } 607 608 /* Schedule timer to hangup call. */ 609 if (pjsua.config.uas_duration > 0) { 610 schedule_call_timer( &pjsua.calls[call_index], 611 &pjsua.calls[call_index].hangup_tm, 612 HANGUP_CALL_TIMER, 613 pjsua.config.uas_duration); 614 } 493 ++pjsua_var.call_cnt; 494 615 495 616 496 /* Notify application */ 617 if (pjsua.cb.on_incoming_call) 618 pjsua.cb.on_incoming_call(acc_index, call_index, rdata); 619 497 if (pjsua_var.ua_cfg.cb.on_incoming_call) 498 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata); 620 499 621 500 /* This INVITE request has been handled. */ … … 624 503 625 504 505 506 /* 507 * Check if the specified call has active INVITE session and the INVITE 508 * session has not been disconnected. 509 */ 510 PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id) 511 { 512 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 513 PJ_EINVAL); 514 return pjsua_var.calls[call_id].inv != NULL && 515 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED; 516 } 517 518 519 /* 520 * Check if call has an active media session. 521 */ 522 PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id) 523 { 524 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 525 PJ_EINVAL); 526 return pjsua_var.calls[call_id].session != NULL; 527 } 528 529 530 /* 531 * Get the conference port identification associated with the call. 532 */ 533 PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id) 534 { 535 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 536 PJ_EINVAL); 537 return pjsua_var.calls[call_id].conf_slot; 538 } 539 540 541 /* 542 * Obtain detail information about the specified call. 543 */ 544 PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id, 545 pjsua_call_info *info) 546 { 547 pjsua_call *call; 548 549 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 550 PJ_EINVAL); 551 552 pj_memset(info, 0, sizeof(*info)); 553 554 PJSUA_LOCK(); 555 556 call = &pjsua_var.calls[call_id]; 557 558 if (call->inv == NULL) { 559 PJSUA_UNLOCK(); 560 return PJ_SUCCESS; 561 } 562 563 pjsip_dlg_inc_lock(call->inv->dlg); 564 565 566 /* id and role */ 567 info->id = call_id; 568 info->role = call->inv->role; 569 570 /* local info */ 571 info->local_info.ptr = info->buf_.local_info; 572 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str, 573 sizeof(info->buf_.local_info)); 574 575 /* local contact */ 576 info->local_contact.ptr = info->buf_.local_contact; 577 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, 578 call->inv->dlg->local.contact->uri, 579 info->local_contact.ptr, 580 sizeof(info->buf_.local_contact)); 581 582 /* remote info */ 583 info->remote_info.ptr = info->buf_.remote_info; 584 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str, 585 sizeof(info->buf_.remote_info)); 586 587 /* remote contact */ 588 if (call->inv->dlg->remote.contact) { 589 int len; 590 info->remote_contact.ptr = info->buf_.remote_contact; 591 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, 592 call->inv->dlg->remote.contact->uri, 593 info->remote_contact.ptr, 594 sizeof(info->buf_.remote_contact)); 595 if (len < 0) len = 0; 596 info->remote_contact.slen = len; 597 } else { 598 info->remote_contact.slen = 0; 599 } 600 601 /* call id */ 602 info->call_id.ptr = info->buf_.call_id; 603 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id, 604 sizeof(info->buf_.call_id)); 605 606 /* state, state_text */ 607 info->state = call->inv->state; 608 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state)); 609 610 /* If call is disconnected, set the last_status from the cause code */ 611 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) { 612 /* last_status, last_status_text */ 613 info->last_status = call->inv->cause; 614 615 info->last_status_text.ptr = info->buf_.last_status_text; 616 pj_strncpy(&info->last_status_text, &call->inv->cause_text, 617 sizeof(info->buf_.last_status_text)); 618 } else { 619 /* last_status, last_status_text */ 620 info->last_status = call->last_code; 621 622 info->last_status_text.ptr = info->buf_.last_status_text; 623 pj_strncpy(&info->last_status_text, &call->last_text, 624 sizeof(info->buf_.last_status_text)); 625 } 626 627 /* media status and dir */ 628 info->media_status = call->media_st; 629 info->media_dir = call->media_dir; 630 631 632 /* conference slot number */ 633 info->conf_slot = call->conf_slot; 634 635 /* calculate duration */ 636 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) { 637 638 info->total_duration = call->dis_time; 639 PJ_TIME_VAL_SUB(info->total_duration, call->start_time); 640 641 if (call->conn_time.sec) { 642 info->connect_duration = call->dis_time; 643 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time); 644 } 645 646 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) { 647 648 pj_gettimeofday(&info->total_duration); 649 PJ_TIME_VAL_SUB(info->total_duration, call->start_time); 650 651 pj_gettimeofday(&info->connect_duration); 652 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time); 653 654 } else { 655 pj_gettimeofday(&info->total_duration); 656 PJ_TIME_VAL_SUB(info->total_duration, call->start_time); 657 } 658 659 pjsip_dlg_dec_lock(call->inv->dlg); 660 PJSUA_UNLOCK(); 661 662 return PJ_SUCCESS; 663 } 664 665 666 /* 667 * Attach application specific data to the call. 668 */ 669 PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id, 670 void *user_data) 671 { 672 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 673 PJ_EINVAL); 674 pjsua_var.calls[call_id].user_data = user_data; 675 676 return PJ_SUCCESS; 677 } 678 679 680 /* 681 * Get user data attached to the call. 682 */ 683 PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id) 684 { 685 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 686 NULL); 687 return pjsua_var.calls[call_id].user_data; 688 } 689 690 691 /* 692 * Send response to incoming INVITE request. 693 */ 694 PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id, 695 unsigned code, 696 const pj_str_t *reason, 697 const pjsua_msg_data *msg_data) 698 { 699 pjsua_call *call; 700 pjsip_tx_data *tdata; 701 pj_status_t status; 702 703 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 704 PJ_EINVAL); 705 706 PJSUA_LOCK(); 707 708 call = &pjsua_var.calls[call_id]; 709 710 if (call->inv == NULL) { 711 PJ_LOG(3,(THIS_FILE, "Call %d already disconnected", call_id)); 712 PJSUA_UNLOCK(); 713 return PJSIP_ESESSIONTERMINATED; 714 } 715 716 /* Create response message */ 717 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata); 718 if (status != PJ_SUCCESS) { 719 pjsua_perror(THIS_FILE, "Error creating response", 720 status); 721 PJSUA_UNLOCK(); 722 return status; 723 } 724 725 /* Add additional headers etc */ 726 pjsua_process_msg_data( tdata, msg_data); 727 728 /* Send the message */ 729 status = pjsip_inv_send_msg(call->inv, tdata); 730 if (status != PJ_SUCCESS) 731 pjsua_perror(THIS_FILE, "Error sending response", 732 status); 733 734 PJSUA_UNLOCK(); 735 736 return status; 737 } 738 739 740 /* 741 * Hangup call by using method that is appropriate according to the 742 * call state. 743 */ 744 PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id, 745 unsigned code, 746 const pj_str_t *reason, 747 const pjsua_msg_data *msg_data) 748 { 749 pjsua_call *call; 750 pj_status_t status; 751 pjsip_tx_data *tdata; 752 753 754 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 755 PJ_EINVAL); 756 757 PJSUA_LOCK(); 758 759 call = &pjsua_var.calls[call_id]; 760 761 if (!call->inv) { 762 PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 763 PJSUA_UNLOCK(); 764 return PJ_EINVAL; 765 } 766 767 if (code==0) { 768 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED) 769 code = PJSIP_SC_OK; 770 else if (call->inv->role == PJSIP_ROLE_UAS) 771 code = PJSIP_SC_DECLINE; 772 else 773 code = PJSIP_SC_REQUEST_TERMINATED; 774 } 775 776 status = pjsip_inv_end_session(call->inv, code, reason, &tdata); 777 if (status != PJ_SUCCESS) { 778 pjsua_perror(THIS_FILE, 779 "Failed to create end session message", 780 status); 781 PJSUA_UNLOCK(); 782 return status; 783 } 784 785 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL 786 * as p_tdata when INVITE transaction has not been answered 787 * with any provisional responses. 788 */ 789 if (tdata == NULL) { 790 PJSUA_UNLOCK(); 791 return PJ_SUCCESS; 792 } 793 794 /* Add additional headers etc */ 795 pjsua_process_msg_data( tdata, msg_data); 796 797 /* Send the message */ 798 status = pjsip_inv_send_msg(call->inv, tdata); 799 if (status != PJ_SUCCESS) { 800 pjsua_perror(THIS_FILE, 801 "Failed to send end session message", 802 status); 803 PJSUA_UNLOCK(); 804 return status; 805 } 806 807 PJSUA_UNLOCK(); 808 809 return PJ_SUCCESS; 810 } 811 812 813 /* 814 * Put the specified call on hold. 815 */ 816 PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id, 817 const pjsua_msg_data *msg_data) 818 { 819 pjmedia_sdp_session *sdp; 820 pjsua_call *call; 821 pjsip_tx_data *tdata; 822 pj_status_t status; 823 824 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 825 PJ_EINVAL); 826 827 PJSUA_LOCK(); 828 829 call = &pjsua_var.calls[call_id]; 830 831 if (!call->inv) { 832 PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 833 PJSUA_UNLOCK(); 834 return PJSIP_ESESSIONTERMINATED; 835 } 836 837 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) { 838 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed")); 839 PJSUA_UNLOCK(); 840 return PJSIP_ESESSIONSTATE; 841 } 842 843 status = create_inactive_sdp(call, &sdp); 844 if (status != PJ_SUCCESS) { 845 PJSUA_UNLOCK(); 846 return status; 847 } 848 849 /* Create re-INVITE with new offer */ 850 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata); 851 if (status != PJ_SUCCESS) { 852 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status); 853 PJSUA_UNLOCK(); 854 return status; 855 } 856 857 /* Add additional headers etc */ 858 pjsua_process_msg_data( tdata, msg_data); 859 860 /* Send the request */ 861 status = pjsip_inv_send_msg( call->inv, tdata); 862 if (status != PJ_SUCCESS) { 863 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status); 864 PJSUA_UNLOCK(); 865 return status; 866 } 867 868 PJSUA_UNLOCK(); 869 870 return PJ_SUCCESS; 871 } 872 873 874 /* 875 * Send re-INVITE (to release hold). 876 */ 877 PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id, 878 pj_bool_t unhold, 879 const pjsua_msg_data *msg_data) 880 { 881 pjmedia_sdp_session *sdp; 882 pjsip_tx_data *tdata; 883 pjsua_call *call; 884 pj_status_t status; 885 886 887 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 888 PJ_EINVAL); 889 890 PJSUA_LOCK(); 891 892 call = &pjsua_var.calls[call_id]; 893 894 if (!call->inv) { 895 PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 896 PJSUA_UNLOCK(); 897 return PJSIP_ESESSIONTERMINATED; 898 } 899 900 901 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) { 902 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed")); 903 PJSUA_UNLOCK(); 904 return PJSIP_ESESSIONSTATE; 905 } 906 907 /* Create SDP */ 908 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, call->inv->pool, 909 1, &call->skinfo, &sdp); 910 if (status != PJ_SUCCESS) { 911 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint", 912 status); 913 PJSUA_UNLOCK(); 914 return status; 915 } 916 917 /* Create re-INVITE with new offer */ 918 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata); 919 if (status != PJ_SUCCESS) { 920 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status); 921 PJSUA_UNLOCK(); 922 return status; 923 } 924 925 /* Add additional headers etc */ 926 pjsua_process_msg_data( tdata, msg_data); 927 928 /* Send the request */ 929 status = pjsip_inv_send_msg( call->inv, tdata); 930 if (status != PJ_SUCCESS) { 931 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status); 932 PJSUA_UNLOCK(); 933 return status; 934 } 935 936 PJSUA_UNLOCK(); 937 938 return PJ_SUCCESS; 939 } 940 941 942 /* 943 * Initiate call transfer to the specified address. 944 */ 945 PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id, 946 const pj_str_t *dest, 947 const pjsua_msg_data *msg_data) 948 { 949 pjsip_evsub *sub; 950 pjsip_tx_data *tdata; 951 pjsua_call *call; 952 pj_status_t status; 953 954 955 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 956 PJ_EINVAL); 957 958 PJSUA_LOCK(); 959 960 call = &pjsua_var.calls[call_id]; 961 962 if (!call->inv) { 963 PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 964 PJSUA_UNLOCK(); 965 return PJSIP_ESESSIONTERMINATED; 966 } 967 968 /* Create xfer client subscription. 969 * We're not interested in knowing the transfer result, so we 970 * put NULL as the callback. 971 */ 972 status = pjsip_xfer_create_uac(call->inv->dlg, NULL, &sub); 973 if (status != PJ_SUCCESS) { 974 pjsua_perror(THIS_FILE, "Unable to create xfer", status); 975 PJSUA_UNLOCK(); 976 return status; 977 } 978 979 /* 980 * Create REFER request. 981 */ 982 status = pjsip_xfer_initiate(sub, dest, &tdata); 983 if (status != PJ_SUCCESS) { 984 pjsua_perror(THIS_FILE, "Unable to create REFER request", status); 985 PJSUA_UNLOCK(); 986 return status; 987 } 988 989 /* Add additional headers etc */ 990 pjsua_process_msg_data( tdata, msg_data); 991 992 /* Send. */ 993 status = pjsip_xfer_send_request(sub, tdata); 994 if (status != PJ_SUCCESS) { 995 pjsua_perror(THIS_FILE, "Unable to send REFER request", status); 996 PJSUA_UNLOCK(); 997 return status; 998 } 999 1000 /* For simplicity (that's what this program is intended to be!), 1001 * leave the original invite session as it is. More advanced application 1002 * may want to hold the INVITE, or terminate the invite, or whatever. 1003 */ 1004 1005 PJSUA_UNLOCK(); 1006 1007 return PJ_SUCCESS; 1008 1009 } 1010 1011 1012 /* 1013 * Send DTMF digits to remote using RFC 2833 payload formats. 1014 */ 1015 PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id, 1016 const pj_str_t *digits) 1017 { 1018 pjsua_call *call; 1019 pj_status_t status; 1020 1021 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 1022 PJ_EINVAL); 1023 1024 PJSUA_LOCK(); 1025 1026 call = &pjsua_var.calls[call_id]; 1027 1028 if (!call->session) { 1029 PJ_LOG(3,(THIS_FILE, "Media is not established yet!")); 1030 PJSUA_UNLOCK(); 1031 return PJ_EINVALIDOP; 1032 } 1033 1034 status = pjmedia_session_dial_dtmf( call->session, 0, digits); 1035 1036 PJSUA_UNLOCK(); 1037 1038 return status; 1039 } 1040 1041 1042 /** 1043 * Send instant messaging inside INVITE session. 1044 */ 1045 PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id, 1046 const pj_str_t *mime_type, 1047 const pj_str_t *content, 1048 const pjsua_msg_data *msg_data, 1049 void *user_data) 1050 { 1051 pjsua_call *call; 1052 const pj_str_t mime_text_plain = pj_str("text/plain"); 1053 pjsip_media_type ctype; 1054 pjsua_im_data *im_data; 1055 pjsip_tx_data *tdata; 1056 pj_status_t status; 1057 1058 1059 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 1060 PJ_EINVAL); 1061 1062 PJSUA_LOCK(); 1063 1064 call = &pjsua_var.calls[call_id]; 1065 1066 if (!call->inv) { 1067 PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 1068 PJSUA_UNLOCK(); 1069 return PJSIP_ESESSIONTERMINATED; 1070 } 1071 1072 /* Lock dialog. */ 1073 pjsip_dlg_inc_lock(call->inv->dlg); 1074 1075 /* Set default media type if none is specified */ 1076 if (mime_type == NULL) { 1077 mime_type = &mime_text_plain; 1078 } 1079 1080 /* Create request message. */ 1081 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method, 1082 -1, &tdata); 1083 if (status != PJ_SUCCESS) { 1084 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status); 1085 goto on_return; 1086 } 1087 1088 /* Add accept header. */ 1089 pjsip_msg_add_hdr( tdata->msg, 1090 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool)); 1091 1092 /* Parse MIME type */ 1093 pjsua_parse_media_type(tdata->pool, mime_type, &ctype); 1094 1095 /* Create "text/plain" message body. */ 1096 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type, 1097 &ctype.subtype, content); 1098 if (tdata->msg->body == NULL) { 1099 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM); 1100 pjsip_tx_data_dec_ref(tdata); 1101 goto on_return; 1102 } 1103 1104 /* Add additional headers etc */ 1105 pjsua_process_msg_data( tdata, msg_data); 1106 1107 /* Create IM data and attach to the request. */ 1108 im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data)); 1109 im_data->acc_id = call->acc_id; 1110 im_data->call_id = call_id; 1111 im_data->to = call->inv->dlg->remote.info_str; 1112 pj_strdup_with_null(tdata->pool, &im_data->body, content); 1113 im_data->user_data = user_data; 1114 1115 1116 /* Send the request. */ 1117 status = pjsip_dlg_send_request( call->inv->dlg, tdata, 1118 pjsua_var.mod.id, im_data); 1119 if (status != PJ_SUCCESS) { 1120 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status); 1121 goto on_return; 1122 } 1123 1124 on_return: 1125 pjsip_dlg_dec_lock(call->inv->dlg); 1126 PJSUA_UNLOCK(); 1127 return status; 1128 } 1129 1130 1131 /* 1132 * Send IM typing indication inside INVITE session. 1133 */ 1134 PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id, 1135 pj_bool_t is_typing, 1136 const pjsua_msg_data*msg_data) 1137 { 1138 pjsua_call *call; 1139 pjsip_tx_data *tdata; 1140 pj_status_t status; 1141 1142 1143 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 1144 PJ_EINVAL); 1145 1146 PJSUA_LOCK(); 1147 1148 call = &pjsua_var.calls[call_id]; 1149 1150 if (!call->inv) { 1151 PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 1152 PJSUA_UNLOCK(); 1153 return PJSIP_ESESSIONTERMINATED; 1154 } 1155 1156 /* Lock dialog. */ 1157 pjsip_dlg_inc_lock(call->inv->dlg); 1158 1159 /* Create request message. */ 1160 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method, 1161 -1, &tdata); 1162 if (status != PJ_SUCCESS) { 1163 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status); 1164 goto on_return; 1165 } 1166 1167 /* Create "application/im-iscomposing+xml" msg body. */ 1168 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing, 1169 NULL, NULL, -1); 1170 1171 /* Add additional headers etc */ 1172 pjsua_process_msg_data( tdata, msg_data); 1173 1174 /* Send the request. */ 1175 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL); 1176 if (status != PJ_SUCCESS) { 1177 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status); 1178 goto on_return; 1179 } 1180 1181 on_return: 1182 pjsip_dlg_dec_lock(call->inv->dlg); 1183 PJSUA_UNLOCK(); 1184 return status; 1185 } 1186 1187 1188 /* 1189 * Terminate all calls. 1190 */ 1191 PJ_DEF(void) pjsua_call_hangup_all(void) 1192 { 1193 unsigned i; 1194 1195 PJSUA_LOCK(); 1196 1197 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { 1198 if (pjsua_var.calls[i].inv) 1199 pjsua_call_hangup(i, 0, NULL, NULL); 1200 } 1201 1202 PJSUA_UNLOCK(); 1203 } 1204 1205 1206 static const char *good_number(char *buf, pj_int32_t val) 1207 { 1208 if (val < 1000) { 1209 pj_ansi_sprintf(buf, "%d", val); 1210 } else if (val < 1000000) { 1211 pj_ansi_sprintf(buf, "%d.%dK", 1212 val / 1000, 1213 (val % 1000) / 100); 1214 } else { 1215 pj_ansi_sprintf(buf, "%d.%02dM", 1216 val / 1000000, 1217 (val % 1000000) / 10000); 1218 } 1219 1220 return buf; 1221 } 1222 1223 1224 /* Dump media session */ 1225 static void dump_media_session(const char *indent, 1226 char *buf, unsigned maxlen, 1227 pjmedia_session *session) 1228 { 1229 unsigned i; 1230 char *p = buf, *end = buf+maxlen; 1231 int len; 1232 pjmedia_session_info info; 1233 1234 pjmedia_session_get_info(session, &info); 1235 1236 for (i=0; i<info.stream_cnt; ++i) { 1237 pjmedia_rtcp_stat stat; 1238 const char *rem_addr; 1239 int rem_port; 1240 const char *dir; 1241 char last_update[40]; 1242 char packets[16], bytes[16], ipbytes[16]; 1243 pj_time_val now; 1244 1245 pjmedia_session_get_stream_stat(session, i, &stat); 1246 rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr); 1247 rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port); 1248 1249 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING) 1250 dir = "sendonly"; 1251 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING) 1252 dir = "recvonly"; 1253 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING) 1254 dir = "sendrecv"; 1255 else 1256 dir = "inactive"; 1257 1258 1259 len = pj_ansi_snprintf(buf, end-p, 1260 "%s #%d %.*s @%dKHz, %s, peer=%s:%d", 1261 indent, i, 1262 info.stream_info[i].fmt.encoding_name.slen, 1263 info.stream_info[i].fmt.encoding_name.ptr, 1264 info.stream_info[i].fmt.clock_rate / 1000, 1265 dir, 1266 rem_addr, rem_port); 1267 if (len < 1 || len > end-p) { 1268 *p = '\0'; 1269 return; 1270 } 1271 1272 p += len; 1273 *p++ = '\n'; 1274 *p = '\0'; 1275 1276 if (stat.rx.update_cnt == 0) 1277 strcpy(last_update, "never"); 1278 else { 1279 pj_gettimeofday(&now); 1280 PJ_TIME_VAL_SUB(now, stat.rx.update); 1281 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", 1282 now.sec / 3600, 1283 (now.sec % 3600) / 60, 1284 now.sec % 60, 1285 now.msec); 1286 } 1287 1288 len = pj_ansi_snprintf(p, end-p, 1289 "%s RX pt=%d, stat last update: %s\n" 1290 "%s total %spkt %sB (%sB +IP hdr)\n" 1291 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n" 1292 "%s (msec) min avg max last\n" 1293 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n" 1294 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s", 1295 indent, info.stream_info[i].fmt.pt, 1296 last_update, 1297 indent, 1298 good_number(packets, stat.rx.pkt), 1299 good_number(bytes, stat.rx.bytes), 1300 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32), 1301 indent, 1302 stat.rx.loss, 1303 stat.rx.loss * 100.0 / stat.rx.pkt, 1304 stat.rx.dup, 1305 stat.rx.dup * 100.0 / stat.rx.pkt, 1306 stat.rx.reorder, 1307 stat.rx.reorder * 100.0 / stat.rx.pkt, 1308 indent, indent, 1309 stat.rx.loss_period.min / 1000.0, 1310 stat.rx.loss_period.avg / 1000.0, 1311 stat.rx.loss_period.max / 1000.0, 1312 stat.rx.loss_period.last / 1000.0, 1313 indent, 1314 stat.rx.jitter.min / 1000.0, 1315 stat.rx.jitter.avg / 1000.0, 1316 stat.rx.jitter.max / 1000.0, 1317 stat.rx.jitter.last / 1000.0, 1318 "" 1319 ); 1320 1321 if (len < 1 || len > end-p) { 1322 *p = '\0'; 1323 return; 1324 } 1325 1326 p += len; 1327 *p++ = '\n'; 1328 *p = '\0'; 1329 1330 if (stat.tx.update_cnt == 0) 1331 strcpy(last_update, "never"); 1332 else { 1333 pj_gettimeofday(&now); 1334 PJ_TIME_VAL_SUB(now, stat.tx.update); 1335 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", 1336 now.sec / 3600, 1337 (now.sec % 3600) / 60, 1338 now.sec % 60, 1339 now.msec); 1340 } 1341 1342 len = pj_ansi_snprintf(p, end-p, 1343 "%s TX pt=%d, ptime=%dms, stat last update: %s\n" 1344 "%s total %spkt %sB (%sB +IP hdr)\n" 1345 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n" 1346 "%s (msec) min avg max last\n" 1347 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n" 1348 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s", 1349 indent, 1350 info.stream_info[i].tx_pt, 1351 info.stream_info[i].param->info.frm_ptime * 1352 info.stream_info[i].param->setting.frm_per_pkt, 1353 last_update, 1354 1355 indent, 1356 good_number(packets, stat.tx.pkt), 1357 good_number(bytes, stat.tx.bytes), 1358 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32), 1359 1360 indent, 1361 stat.tx.loss, 1362 stat.tx.loss * 100.0 / stat.tx.pkt, 1363 stat.tx.dup, 1364 stat.tx.dup * 100.0 / stat.tx.pkt, 1365 stat.tx.reorder, 1366 stat.tx.reorder * 100.0 / stat.tx.pkt, 1367 1368 indent, indent, 1369 stat.tx.loss_period.min / 1000.0, 1370 stat.tx.loss_period.avg / 1000.0, 1371 stat.tx.loss_period.max / 1000.0, 1372 stat.tx.loss_period.last / 1000.0, 1373 indent, 1374 stat.tx.jitter.min / 1000.0, 1375 stat.tx.jitter.avg / 1000.0, 1376 stat.tx.jitter.max / 1000.0, 1377 stat.tx.jitter.last / 1000.0, 1378 "" 1379 ); 1380 1381 if (len < 1 || len > end-p) { 1382 *p = '\0'; 1383 return; 1384 } 1385 1386 p += len; 1387 *p++ = '\n'; 1388 *p = '\0'; 1389 1390 len = pj_ansi_snprintf(p, end-p, 1391 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f", 1392 indent, 1393 stat.rtt.min / 1000.0, 1394 stat.rtt.avg / 1000.0, 1395 stat.rtt.max / 1000.0, 1396 stat.rtt.last / 1000.0 1397 ); 1398 if (len < 1 || len > end-p) { 1399 *p = '\0'; 1400 return; 1401 } 1402 1403 p += len; 1404 *p++ = '\n'; 1405 *p = '\0'; 1406 } 1407 } 1408 1409 1410 /* Print call info */ 1411 static void print_call(const char *title, 1412 int call_id, 1413 char *buf, pj_size_t size) 1414 { 1415 int len; 1416 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv; 1417 pjsip_dialog *dlg = inv->dlg; 1418 char userinfo[128]; 1419 1420 /* Dump invite sesion info. */ 1421 1422 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo)); 1423 if (len < 1) 1424 pj_ansi_strcpy(userinfo, "<--uri too long-->"); 1425 else 1426 userinfo[len] = '\0'; 1427 1428 len = pj_ansi_snprintf(buf, size, "%s[%s] %s", 1429 title, 1430 pjsip_inv_state_name(inv->state), 1431 userinfo); 1432 if (len < 1 || len >= (int)size) { 1433 pj_ansi_strcpy(buf, "<--uri too long-->"); 1434 len = 18; 1435 } else 1436 buf[len] = '\0'; 1437 } 1438 1439 1440 /* 1441 * Dump call and media statistics to string. 1442 */ 1443 PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id, 1444 pj_bool_t with_media, 1445 char *buffer, 1446 unsigned maxlen, 1447 const char *indent) 1448 { 1449 pjsua_call *call; 1450 pj_time_val duration, res_delay, con_delay; 1451 char tmp[128]; 1452 char *p, *end; 1453 int len; 1454 1455 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 1456 PJ_EINVAL); 1457 1458 PJSUA_LOCK(); 1459 1460 call = &pjsua_var.calls[call_id]; 1461 1462 *buffer = '\0'; 1463 p = buffer; 1464 end = buffer + maxlen; 1465 len = 0; 1466 1467 if (call->inv == NULL) { 1468 PJSUA_UNLOCK(); 1469 return PJ_EINVALIDOP; 1470 } 1471 1472 print_call(indent, call_id, tmp, sizeof(tmp)); 1473 1474 len = pj_ansi_strlen(tmp); 1475 pj_ansi_strcpy(buffer, tmp); 1476 1477 p += len; 1478 *p++ = '\r'; 1479 *p++ = '\n'; 1480 1481 /* Calculate call duration */ 1482 if (call->inv->state >= PJSIP_INV_STATE_CONFIRMED) { 1483 pj_gettimeofday(&duration); 1484 PJ_TIME_VAL_SUB(duration, call->conn_time); 1485 con_delay = call->conn_time; 1486 PJ_TIME_VAL_SUB(con_delay, call->start_time); 1487 } else { 1488 duration.sec = duration.msec = 0; 1489 con_delay.sec = con_delay.msec = 0; 1490 } 1491 1492 /* Calculate first response delay */ 1493 if (call->inv->state >= PJSIP_INV_STATE_EARLY) { 1494 res_delay = call->res_time; 1495 PJ_TIME_VAL_SUB(res_delay, call->start_time); 1496 } else { 1497 res_delay.sec = res_delay.msec = 0; 1498 } 1499 1500 /* Print duration */ 1501 len = pj_ansi_snprintf(p, end-p, 1502 "%s Call time: %02dh:%02dm:%02ds, " 1503 "1st res in %d ms, conn in %dms", 1504 indent, 1505 (duration.sec / 3600), 1506 ((duration.sec % 3600)/60), 1507 (duration.sec % 60), 1508 PJ_TIME_VAL_MSEC(res_delay), 1509 PJ_TIME_VAL_MSEC(con_delay)); 1510 1511 if (len > 0 && len < end-p) { 1512 p += len; 1513 *p++ = '\n'; 1514 *p = '\0'; 1515 } 1516 1517 /* Dump session statistics */ 1518 if (with_media && call->session) 1519 dump_media_session(indent, p, end-p, call->session); 1520 1521 PJSUA_UNLOCK(); 1522 1523 return PJ_SUCCESS; 1524 } 1525 1526 1527 /* 1528 * Destroy the call's media 1529 */ 1530 static pj_status_t call_destroy_media(int call_id) 1531 { 1532 pjsua_call *call = &pjsua_var.calls[call_id]; 1533 1534 if (call->conf_slot != PJSUA_INVALID_ID) { 1535 pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot); 1536 call->conf_slot = PJSUA_INVALID_ID; 1537 } 1538 1539 if (call->session) { 1540 /* Destroy session (this will also close RTP/RTCP sockets). */ 1541 pjmedia_session_destroy(call->session); 1542 call->session = NULL; 1543 1544 PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed", 1545 call_id)); 1546 1547 } 1548 1549 call->media_st = PJSUA_CALL_MEDIA_NONE; 1550 1551 return PJ_SUCCESS; 1552 } 1553 1554 626 1555 /* 627 1556 * This callback receives notification from invite session when the … … 631 1560 pjsip_event *e) 632 1561 { 633 pjsua_call *call = inv->dlg->mod_data[pjsua.mod.id]; 634 635 if (!call) 1562 pjsua_call *call; 1563 1564 PJSUA_LOCK(); 1565 1566 call = inv->dlg->mod_data[pjsua_var.mod.id]; 1567 1568 if (!call) { 1569 PJSUA_UNLOCK(); 636 1570 return; 1571 } 1572 637 1573 638 1574 /* Get call times */ … … 643 1579 pj_gettimeofday(&call->res_time); 644 1580 call->last_code = e->body.tsx_state.tsx->status_code; 1581 pj_strncpy(&call->last_text, 1582 &e->body.tsx_state.tsx->status_text, 1583 sizeof(call->last_text_buf_)); 645 1584 break; 646 1585 case PJSIP_INV_STATE_CONFIRMED: … … 651 1590 if (e->body.tsx_state.tsx->status_code > call->last_code) { 652 1591 call->last_code = e->body.tsx_state.tsx->status_code; 1592 pj_strncpy(&call->last_text, 1593 &e->body.tsx_state.tsx->status_text, 1594 sizeof(call->last_text_buf_)); 653 1595 } 654 1596 break; 655 1597 default: 656 1598 call->last_code = e->body.tsx_state.tsx->status_code; 1599 pj_strncpy(&call->last_text, 1600 &e->body.tsx_state.tsx->status_text, 1601 sizeof(call->last_text_buf_)); 657 1602 break; 658 1603 } … … 717 1662 718 1663 719 if (pjsua .cb.on_call_state)720 (*pjsua .cb.on_call_state)(call->index, e);1664 if (pjsua_var.ua_cfg.cb.on_call_state) 1665 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e); 721 1666 722 1667 /* call->inv may be NULL now */ … … 730 1675 call_destroy_media(call->index); 731 1676 732 /* Remove timers. */733 schedule_call_timer(call, &call->refresh_tm, REFRESH_CALL_TIMER, 0);734 schedule_call_timer(call, &call->hangup_tm, HANGUP_CALL_TIMER, 0);735 736 1677 /* Free call */ 737 1678 call->inv = NULL; 738 --pjsua.call_cnt; 739 } 740 } 741 742 743 /* 744 * Callback called by event framework when the xfer subscription state 745 * has changed. 746 */ 747 static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event) 748 { 749 750 PJ_UNUSED_ARG(event); 751 752 /* 753 * We're only interested when subscription is terminated, to 754 * clear the xfer_sub member of the inv_data. 755 */ 756 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { 757 pjsua_call *call; 758 759 call = pjsip_evsub_get_mod_data(sub, pjsua.mod.id); 760 if (!call) 761 return; 762 763 pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL); 764 call->xfer_sub = NULL; 765 766 PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated")); 767 } 768 } 769 770 771 /* 772 * Follow transfer (REFER) request. 773 */ 774 static void on_call_transfered( pjsip_inv_session *inv, 775 pjsip_rx_data *rdata ) 776 { 777 pj_status_t status; 778 pjsip_tx_data *tdata; 779 pjsua_call *existing_call; 780 int new_call; 781 const pj_str_t str_refer_to = { "Refer-To", 8}; 782 pjsip_generic_string_hdr *refer_to; 783 char *uri; 784 pj_str_t tmp; 785 struct pjsip_evsub_user xfer_cb; 786 pjsip_status_code code; 787 pjsip_evsub *sub; 788 789 existing_call = inv->dlg->mod_data[pjsua.mod.id]; 790 791 /* Find the Refer-To header */ 792 refer_to = (pjsip_generic_string_hdr*) 793 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL); 794 795 if (refer_to == NULL) { 796 /* Invalid Request. 797 * No Refer-To header! 798 */ 799 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!")); 800 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL); 801 return; 802 } 803 804 /* Notify callback */ 805 code = PJSIP_SC_OK; 806 if (pjsua.cb.on_call_transfered) 807 (*pjsua.cb.on_call_transfered)(existing_call->index, 808 &refer_to->hvalue, &code); 809 810 if (code < 200) 811 code = 200; 812 if (code >= 300) { 813 /* Application rejects call transfer request */ 814 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL); 815 return; 816 } 817 818 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s", 819 (int)inv->dlg->remote.info_str.slen, 820 inv->dlg->remote.info_str.ptr, 821 (int)refer_to->hvalue.slen, 822 refer_to->hvalue.ptr)); 823 824 /* Init callback */ 825 pj_memset(&xfer_cb, 0, sizeof(xfer_cb)); 826 xfer_cb.on_evsub_state = &xfer_on_evsub_state; 827 828 /* Create transferee event subscription */ 829 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub); 830 if (status != PJ_SUCCESS) { 831 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status); 832 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL); 833 return; 834 } 835 836 /* Accept the REFER request, send 200 (OK). */ 837 pjsip_xfer_accept(sub, rdata, code, NULL); 838 839 /* Create initial NOTIFY request */ 840 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE, 841 100, NULL, &tdata); 842 if (status != PJ_SUCCESS) { 843 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", status); 844 return; 845 } 846 847 /* Send initial NOTIFY request */ 848 status = pjsip_xfer_send_request( sub, tdata); 849 if (status != PJ_SUCCESS) { 850 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status); 851 return; 852 } 853 854 /* We're cheating here. 855 * We need to get a null terminated string from a pj_str_t. 856 * So grab the pointer from the hvalue and NULL terminate it, knowing 857 * that the NULL position will be occupied by a newline. 858 */ 859 uri = refer_to->hvalue.ptr; 860 uri[refer_to->hvalue.slen] = '\0'; 861 862 /* Now make the outgoing call. */ 863 tmp = pj_str(uri); 864 status = pjsua_call_make_call(existing_call->acc_index, &tmp, &new_call); 865 if (status != PJ_SUCCESS) { 866 867 /* Notify xferer about the error */ 868 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED, 869 500, NULL, &tdata); 870 if (status != PJ_SUCCESS) { 871 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", 872 status); 873 return; 874 } 875 status = pjsip_xfer_send_request(sub, tdata); 876 if (status != PJ_SUCCESS) { 877 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", 878 status); 879 return; 880 } 881 return; 882 } 883 884 /* Put the server subscription in inv_data. 885 * Subsequent state changed in pjsua_inv_on_state_changed() will be 886 * reported back to the server subscription. 887 */ 888 pjsua.calls[new_call].xfer_sub = sub; 889 890 /* Put the invite_data in the subscription. */ 891 pjsip_evsub_set_mod_data(sub, pjsua.mod.id, &pjsua.calls[new_call]); 892 } 893 894 895 /* 896 * This callback is called when transaction state has changed in INVITE 897 * session. We use this to trap: 898 * - incoming REFER request. 899 * - incoming MESSAGE request. 900 */ 901 static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv, 902 pjsip_transaction *tsx, 903 pjsip_event *e) 904 { 905 pjsua_call *call = inv->dlg->mod_data[pjsua.mod.id]; 906 907 if (tsx->role==PJSIP_ROLE_UAS && 908 tsx->state==PJSIP_TSX_STATE_TRYING && 909 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0) 910 { 911 /* 912 * Incoming REFER request. 913 */ 914 on_call_transfered(call->inv, e->body.tsx_state.src.rdata); 915 916 } 917 else if (tsx->role==PJSIP_ROLE_UAS && 918 tsx->state==PJSIP_TSX_STATE_TRYING && 919 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0) 920 { 921 /* 922 * Incoming MESSAGE request! 923 */ 924 pjsip_rx_data *rdata; 925 pjsip_msg *msg; 926 pjsip_accept_hdr *accept_hdr; 927 pj_status_t status; 928 929 rdata = e->body.tsx_state.src.rdata; 930 msg = rdata->msg_info.msg; 931 932 /* Request MUST have message body, with Content-Type equal to 933 * "text/plain". 934 */ 935 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) { 936 937 pjsip_hdr hdr_list; 938 939 pj_list_init(&hdr_list); 940 pj_list_push_back(&hdr_list, accept_hdr); 941 942 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE, 943 NULL, &hdr_list, NULL ); 944 return; 945 } 946 947 /* Respond with 200 first, so that remote doesn't retransmit in case 948 * the UI takes too long to process the message. 949 */ 950 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL); 951 952 /* Process MESSAGE request */ 953 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str, 954 &inv->dlg->local.info_str, rdata); 955 } 956 957 } 958 1679 --pjsua_var.call_cnt; 1680 } 1681 1682 PJSUA_UNLOCK(); 1683 } 959 1684 960 1685 /* … … 971 1696 } 972 1697 973 974 /*975 * Create inactive SDP for call hold.976 */977 static pj_status_t create_inactive_sdp(pjsua_call *call,978 pjmedia_sdp_session **p_answer)979 {980 pj_status_t status;981 pjmedia_sdp_conn *conn;982 pjmedia_sdp_attr *attr;983 pjmedia_sdp_session *sdp;984 985 /* Create new offer */986 status = pjmedia_endpt_create_sdp(pjsua.med_endpt, pjsua.pool, 1,987 &call->skinfo, &sdp);988 if (status != PJ_SUCCESS) {989 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);990 return status;991 }992 993 /* Get SDP media connection line */994 conn = sdp->media[0]->conn;995 if (!conn)996 conn = sdp->conn;997 998 /* Modify address */999 conn->addr = pj_str("0.0.0.0");1000 1001 /* Remove existing directions attributes */1002 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");1003 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");1004 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");1005 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");1006 1007 /* Add inactive attribute */1008 attr = pjmedia_sdp_attr_create(pjsua.pool, "inactive", NULL);1009 pjmedia_sdp_media_add_attr(sdp->media[0], attr);1010 1011 *p_answer = sdp;1012 1013 return status;1014 }1015 1016 /*1017 * Called when session received new offer.1018 */1019 static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,1020 const pjmedia_sdp_session *offer)1021 {1022 pjsua_call *call;1023 pjmedia_sdp_conn *conn;1024 pjmedia_sdp_session *answer;1025 pj_bool_t is_remote_active;1026 pj_status_t status;1027 1028 call = inv->dlg->mod_data[pjsua.mod.id];1029 1030 /*1031 * See if remote is offering active media (i.e. not on-hold)1032 */1033 is_remote_active = PJ_TRUE;1034 1035 conn = offer->media[0]->conn;1036 if (!conn)1037 conn = offer->conn;1038 1039 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||1040 pj_strcmp2(&conn->addr, "0")==0)1041 {1042 is_remote_active = PJ_FALSE;1043 1044 }1045 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))1046 {1047 is_remote_active = PJ_FALSE;1048 }1049 1050 PJ_LOG(4,(THIS_FILE, "Received SDP offer, remote media is %s",1051 (is_remote_active ? "active" : "inactive")));1052 1053 /* Supply candidate answer */1054 if (is_remote_active) {1055 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, call->inv->pool, 1,1056 &call->skinfo, &answer);1057 } else {1058 status = create_inactive_sdp( call, &answer );1059 }1060 1061 if (status != PJ_SUCCESS) {1062 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);1063 return;1064 }1065 1066 status = pjsip_inv_set_sdp_answer(call->inv, answer);1067 if (status != PJ_SUCCESS) {1068 pjsua_perror(THIS_FILE, "Unable to set answer", status);1069 return;1070 }1071 1072 }1073 1074 #if 01075 /* Disconnect call */1076 static void call_disconnect(pjsip_inv_session *inv,1077 int st_code)1078 {1079 pjsip_tx_data *tdata;1080 pj_status_t status;1081 1082 status = pjsip_inv_end_session(inv, st_code, NULL, &tdata);1083 if (status == PJ_SUCCESS)1084 status = pjsip_inv_send_msg(inv, tdata);1085 1086 if (status != PJ_SUCCESS) {1087 pjsua_perror(THIS_FILE, "Unable to disconnect call", status);1088 }1089 }1090 #endif1091 1698 1092 1699 /* … … 1098 1705 pj_status_t status) 1099 1706 { 1707 int prev_media_st = 0; 1100 1708 pjsua_call *call; 1101 1709 pjmedia_session_info sess_info; … … 1106 1714 char tmp[PJSIP_MAX_URL_SIZE]; 1107 1715 1108 call = inv->dlg->mod_data[pjsua.mod.id]; 1716 PJSUA_LOCK(); 1717 1718 call = inv->dlg->mod_data[pjsua_var.mod.id]; 1109 1719 1110 1720 if (status != PJ_SUCCESS) { … … 1120 1730 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); 1121 1731 } 1732 1733 PJSUA_UNLOCK(); 1122 1734 return; 1123 1124 1735 } 1125 1736 1126 1737 /* Destroy existing media session, if any. */ 1127 1738 1128 if (call) 1739 if (call) { 1740 prev_media_st = call->media_st; 1129 1741 call_destroy_media(call->index); 1742 } 1130 1743 1131 1744 /* Get local and remote SDP */ … … 1137 1750 status); 1138 1751 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); 1752 PJSUA_UNLOCK(); 1139 1753 return; 1140 1754 } … … 1147 1761 status); 1148 1762 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); 1763 PJSUA_UNLOCK(); 1149 1764 return; 1150 1765 } 1151 1152 if (pjsua.config.null_audio)1153 return;1154 1766 1155 1767 /* Create media session info based on SDP parameters. … … 1157 1769 */ 1158 1770 status = pjmedia_session_info_from_sdp( call->inv->dlg->pool, 1159 pjsua .med_endpt,1771 pjsua_var.med_endpt, 1160 1772 1,&sess_info, 1161 1773 local_sdp, remote_sdp); … … 1164 1776 status); 1165 1777 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); 1778 PJSUA_UNLOCK(); 1166 1779 return; 1167 1780 } 1168 1781 1169 /* Override ptime, if this option is specified. */ 1170 if (pjsua.config.ptime) { 1171 sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t) 1172 (pjsua.config.ptime / 1173 sess_info.stream_info[0].param->info.frm_ptime); 1174 if (sess_info.stream_info[0].param->setting.frm_per_pkt==0) 1175 sess_info.stream_info[0].param->setting.frm_per_pkt = 1; 1176 } 1177 1178 /* Optionally, application may modify other stream settings here 1179 * (such as jitter buffer parameters, codec ptime, etc.) 1180 */ 1181 1182 /* Create session based on session info. */ 1183 status = pjmedia_session_create( pjsua.med_endpt, &sess_info, 1184 &call->med_tp, 1185 call, &call->session ); 1186 if (status != PJ_SUCCESS) { 1187 pjsua_perror(THIS_FILE, "Unable to create media session", 1188 status); 1189 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); 1190 return; 1191 } 1192 1193 1194 /* Get the port interface of the first stream in the session. 1195 * We need the port interface to add to the conference bridge. 1196 */ 1197 pjmedia_session_get_port(call->session, 0, &media_port); 1198 1199 1200 /* 1201 * Add the call to conference bridge. 1202 */ 1203 port_name.ptr = tmp; 1204 port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, 1205 call->inv->dlg->remote.info->uri, 1206 tmp, sizeof(tmp)); 1207 if (port_name.slen < 1) { 1208 port_name = pj_str("call"); 1209 } 1210 status = pjmedia_conf_add_port( pjsua.mconf, call->inv->pool, 1211 media_port, 1212 &port_name, 1213 &call->conf_slot); 1214 if (status != PJ_SUCCESS) { 1215 pjsua_perror(THIS_FILE, "Unable to create conference slot", 1216 status); 1217 call_destroy_media(call->index); 1218 //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR); 1219 return; 1220 } 1221 1222 /* If auto-play is configured, connect the call to the file player 1223 * port 1224 */ 1225 if (pjsua.config.auto_play && pjsua.config.wav_file.slen && 1226 call->inv->role == PJSIP_ROLE_UAS) 1782 /* Check if media is put on-hold */ 1783 if (sess_info.stream_cnt == 0 || 1784 sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE) 1227 1785 { 1228 1786 1229 pjmedia_conf_connect_port( pjsua.mconf, pjsua.player[0].slot, 1230 call->conf_slot, 0); 1231 1232 } 1233 if (pjsua.config.auto_loop && call->inv->role == PJSIP_ROLE_UAS) { 1234 1235 pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 1236 call->conf_slot, 0); 1237 1238 } 1239 if (pjsua.config.auto_conf) { 1240 unsigned i; 1241 1242 pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot, 0); 1243 pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0, 0); 1244 1245 for (i=0; i < pjsua.config.max_calls; ++i) { 1246 1247 if (!pjsua.calls[i].session) 1248 continue; 1249 1250 pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 1251 pjsua.calls[i].conf_slot, 0); 1252 pjmedia_conf_connect_port( pjsua.mconf, pjsua.calls[i].conf_slot, 1253 call->conf_slot, 0); 1787 /* Determine who puts the call on-hold */ 1788 if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) { 1789 if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) { 1790 /* It was local who offer hold */ 1791 call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD; 1792 } else { 1793 call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD; 1794 } 1254 1795 } 1255 1796 1256 } 1257 1258 /* Normal operation: if no auto_xx is given, connect new call to 1259 * the sound device port (port zero) in the main conference bridge. 1260 */ 1261 if (pjsua.config.auto_play == 0 && pjsua.config.auto_loop == 0 && 1262 pjsua.config.auto_conf == 0) 1797 call->media_dir = PJMEDIA_DIR_NONE; 1798 1799 } else { 1800 1801 /* Override ptime, if this option is specified. */ 1802 PJ_TODO(set_codec_ptime_in_call); 1803 1804 1805 /* Optionally, application may modify other stream settings here 1806 * (such as jitter buffer parameters, codec ptime, etc.) 1807 */ 1808 1809 /* Create session based on session info. */ 1810 status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info, 1811 &call->med_tp, 1812 call, &call->session ); 1813 if (status != PJ_SUCCESS) { 1814 pjsua_perror(THIS_FILE, "Unable to create media session", 1815 status); 1816 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); 1817 PJSUA_UNLOCK(); 1818 return; 1819 } 1820 1821 1822 /* Get the port interface of the first stream in the session. 1823 * We need the port interface to add to the conference bridge. 1824 */ 1825 pjmedia_session_get_port(call->session, 0, &media_port); 1826 1827 1828 /* 1829 * Add the call to conference bridge. 1830 */ 1831 port_name.ptr = tmp; 1832 port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, 1833 call->inv->dlg->remote.info->uri, 1834 tmp, sizeof(tmp)); 1835 if (port_name.slen < 1) { 1836 port_name = pj_str("call"); 1837 } 1838 status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool, 1839 media_port, 1840 &port_name, 1841 (unsigned*)&call->conf_slot); 1842 if (status != PJ_SUCCESS) { 1843 pjsua_perror(THIS_FILE, "Unable to create conference slot", 1844 status); 1845 call_destroy_media(call->index); 1846 //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR); 1847 PJSUA_UNLOCK(); 1848 return; 1849 } 1850 1851 /* Call's media state is active */ 1852 call->media_st = PJSUA_CALL_MEDIA_ACTIVE; 1853 call->media_dir = sess_info.stream_info[0].dir; 1854 } 1855 1856 /* Print info. */ 1263 1857 { 1264 pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot, 0);1265 pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0, 0);1266 }1267 1268 1269 /* Done. */1270 {1271 struct pjmedia_session_info sess_info;1272 1858 char info[80]; 1273 1859 int info_len = 0; 1274 1860 unsigned i; 1275 1861 1276 pjmedia_session_get_info(call->session, &sess_info);1277 1862 for (i=0; i<sess_info.stream_cnt; ++i) { 1278 1863 int len; … … 1305 1890 info_len += len; 1306 1891 } 1307 PJ_LOG(3,(THIS_FILE,"Media started%s", info)); 1308 } 1309 } 1310 1311 1312 /* 1313 * Hangup call. 1314 */ 1315 PJ_DEF(void) pjsua_call_hangup(int call_index) 1316 { 1892 PJ_LOG(4,(THIS_FILE,"Media updates%s", info)); 1893 } 1894 1895 /* Call application callback, if any */ 1896 if (pjsua_var.ua_cfg.cb.on_call_media_state) 1897 pjsua_var.ua_cfg.cb.on_call_media_state(call->index); 1898 1899 1900 PJSUA_UNLOCK(); 1901 } 1902 1903 1904 /* 1905 * Create inactive SDP for call hold. 1906 */ 1907 static pj_status_t create_inactive_sdp(pjsua_call *call, 1908 pjmedia_sdp_session **p_answer) 1909 { 1910 pj_status_t status; 1911 pjmedia_sdp_conn *conn; 1912 pjmedia_sdp_attr *attr; 1913 pjmedia_sdp_session *sdp; 1914 1915 /* Create new offer */ 1916 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1, 1917 &call->skinfo, &sdp); 1918 if (status != PJ_SUCCESS) { 1919 pjsua_perror(THIS_FILE, "Unable to create local SDP", status); 1920 return status; 1921 } 1922 1923 /* Get SDP media connection line */ 1924 conn = sdp->media[0]->conn; 1925 if (!conn) 1926 conn = sdp->conn; 1927 1928 /* Modify address */ 1929 conn->addr = pj_str("0.0.0.0"); 1930 1931 /* Remove existing directions attributes */ 1932 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv"); 1933 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly"); 1934 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly"); 1935 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive"); 1936 1937 /* Add inactive attribute */ 1938 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL); 1939 pjmedia_sdp_media_add_attr(sdp->media[0], attr); 1940 1941 *p_answer = sdp; 1942 1943 return status; 1944 } 1945 1946 1947 /* 1948 * Called when session received new offer. 1949 */ 1950 static void pjsua_call_on_rx_offer(pjsip_inv_session *inv, 1951 const pjmedia_sdp_session *offer) 1952 { 1953 const char *remote_state; 1317 1954 pjsua_call *call; 1318 int code; 1955 pjmedia_sdp_conn *conn; 1956 pjmedia_sdp_session *answer; 1957 pj_bool_t is_remote_active; 1958 pj_status_t status; 1959 1960 PJSUA_LOCK(); 1961 1962 call = inv->dlg->mod_data[pjsua_var.mod.id]; 1963 1964 /* 1965 * See if remote is offering active media (i.e. not on-hold) 1966 */ 1967 is_remote_active = PJ_TRUE; 1968 1969 conn = offer->media[0]->conn; 1970 if (!conn) 1971 conn = offer->conn; 1972 1973 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 || 1974 pj_strcmp2(&conn->addr, "0")==0) 1975 { 1976 is_remote_active = PJ_FALSE; 1977 1978 } 1979 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL)) 1980 { 1981 is_remote_active = PJ_FALSE; 1982 } 1983 1984 remote_state = (is_remote_active ? "active" : "inactive"); 1985 1986 /* Supply candidate answer */ 1987 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) { 1988 PJ_LOG(4,(THIS_FILE, 1989 "Call %d: RX new media offer, creating inactive SDP " 1990 "(media in offer is %s)", call->index, remote_state)); 1991 status = create_inactive_sdp( call, &answer ); 1992 } else { 1993 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer", 1994 call->index)); 1995 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, 1996 call->inv->pool, 1, 1997 &call->skinfo, &answer); 1998 } 1999 2000 if (status != PJ_SUCCESS) { 2001 pjsua_perror(THIS_FILE, "Unable to create local SDP", status); 2002 PJSUA_UNLOCK(); 2003 return; 2004 } 2005 2006 status = pjsip_inv_set_sdp_answer(call->inv, answer); 2007 if (status != PJ_SUCCESS) { 2008 pjsua_perror(THIS_FILE, "Unable to set answer", status); 2009 PJSUA_UNLOCK(); 2010 return; 2011 } 2012 2013 PJSUA_UNLOCK(); 2014 } 2015 2016 2017 /* 2018 * Callback called by event framework when the xfer subscription state 2019 * has changed. 2020 */ 2021 static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event) 2022 { 2023 2024 PJ_UNUSED_ARG(event); 2025 2026 /* 2027 * We're only interested when subscription is terminated, to 2028 * clear the xfer_sub member of the inv_data. 2029 */ 2030 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { 2031 pjsua_call *call; 2032 2033 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id); 2034 if (!call) 2035 return; 2036 2037 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL); 2038 call->xfer_sub = NULL; 2039 2040 PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated")); 2041 } 2042 } 2043 2044 2045 /* 2046 * Follow transfer (REFER) request. 2047 */ 2048 static void on_call_transfered( pjsip_inv_session *inv, 2049 pjsip_rx_data *rdata ) 2050 { 1319 2051 pj_status_t status; 1320 2052 pjsip_tx_data *tdata; 1321 1322 1323 call = &pjsua.calls[call_index]; 1324 1325 if (!call->inv) { 1326 PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 2053 pjsua_call *existing_call; 2054 int new_call; 2055 const pj_str_t str_refer_to = { "Refer-To", 8}; 2056 pjsip_generic_string_hdr *refer_to; 2057 char *uri; 2058 pj_str_t tmp; 2059 struct pjsip_evsub_user xfer_cb; 2060 pjsip_status_code code; 2061 pjsip_evsub *sub; 2062 2063 existing_call = inv->dlg->mod_data[pjsua_var.mod.id]; 2064 2065 /* Find the Refer-To header */ 2066 refer_to = (pjsip_generic_string_hdr*) 2067 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL); 2068 2069 if (refer_to == NULL) { 2070 /* Invalid Request. 2071 * No Refer-To header! 2072 */ 2073 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!")); 2074 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL); 1327 2075 return; 1328 2076 } 1329 2077 1330 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED) 1331 code = PJSIP_SC_OK; 1332 else if (call->inv->role == PJSIP_ROLE_UAS) 1333 code = PJSIP_SC_DECLINE; 1334 else 1335 code = PJSIP_SC_REQUEST_TERMINATED; 1336 1337 status = pjsip_inv_end_session(call->inv, code, NULL, &tdata); 1338 if (status != PJ_SUCCESS) { 1339 pjsua_perror(THIS_FILE, 1340 "Failed to create end session message", 1341 status); 2078 /* Notify callback */ 2079 code = PJSIP_SC_OK; 2080 if (pjsua_var.ua_cfg.cb.on_call_transfered) 2081 (*pjsua_var.ua_cfg.cb.on_call_transfered)(existing_call->index, 2082 &refer_to->hvalue, &code); 2083 2084 if (code < 200) 2085 code = 200; 2086 if (code >= 300) { 2087 /* Application rejects call transfer request */ 2088 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL); 1342 2089 return; 1343 2090 } 1344 2091 1345 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL 1346 * as p_tdata when INVITE transaction has not been answered 1347 * with any provisional responses. 2092 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s", 2093 (int)inv->dlg->remote.info_str.slen, 2094 inv->dlg->remote.info_str.ptr, 2095 (int)refer_to->hvalue.slen, 2096 refer_to->hvalue.ptr)); 2097 2098 /* Init callback */ 2099 pj_memset(&xfer_cb, 0, sizeof(xfer_cb)); 2100 xfer_cb.on_evsub_state = &xfer_on_evsub_state; 2101 2102 /* Create transferee event subscription */ 2103 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub); 2104 if (status != PJ_SUCCESS) { 2105 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status); 2106 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL); 2107 return; 2108 } 2109 2110 /* Accept the REFER request, send 200 (OK). */ 2111 pjsip_xfer_accept(sub, rdata, code, NULL); 2112 2113 /* Create initial NOTIFY request */ 2114 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE, 2115 100, NULL, &tdata); 2116 if (status != PJ_SUCCESS) { 2117 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", status); 2118 return; 2119 } 2120 2121 /* Send initial NOTIFY request */ 2122 status = pjsip_xfer_send_request( sub, tdata); 2123 if (status != PJ_SUCCESS) { 2124 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status); 2125 return; 2126 } 2127 2128 /* We're cheating here. 2129 * We need to get a null terminated string from a pj_str_t. 2130 * So grab the pointer from the hvalue and NULL terminate it, knowing 2131 * that the NULL position will be occupied by a newline. 1348 2132 */ 1349 if (tdata == NULL) 2133 uri = refer_to->hvalue.ptr; 2134 uri[refer_to->hvalue.slen] = '\0'; 2135 2136 /* Now make the outgoing call. */ 2137 tmp = pj_str(uri); 2138 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0, 2139 existing_call->user_data, NULL, 2140 &new_call); 2141 if (status != PJ_SUCCESS) { 2142 2143 /* Notify xferer about the error */ 2144 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED, 2145 500, NULL, &tdata); 2146 if (status != PJ_SUCCESS) { 2147 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", 2148 status); 2149 return; 2150 } 2151 status = pjsip_xfer_send_request(sub, tdata); 2152 if (status != PJ_SUCCESS) { 2153 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", 2154 status); 2155 return; 2156 } 1350 2157 return; 1351 1352 status = pjsip_inv_send_msg(call->inv, tdata); 1353 if (status != PJ_SUCCESS) { 1354 pjsua_perror(THIS_FILE, 1355 "Failed to send end session message", 1356 status); 1357 return; 1358 } 1359 } 1360 1361 1362 /* 1363 * Put call on-Hold. 1364 */ 1365 PJ_DEF(pj_status_t) pjsua_call_set_hold(int call_index) 1366 { 1367 pjmedia_sdp_session *sdp; 1368 pjsua_call *call; 1369 pjsip_tx_data *tdata; 1370 pj_status_t status; 1371 1372 call = &pjsua.calls[call_index]; 1373 1374 if (!call->inv) { 1375 PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 1376 return PJSIP_ESESSIONTERMINATED; 1377 } 1378 1379 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) { 1380 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed")); 1381 return PJSIP_ESESSIONSTATE; 1382 } 1383 1384 status = create_inactive_sdp(call, &sdp); 1385 if (status != PJ_SUCCESS) 1386 return status; 1387 1388 /* Send re-INVITE with new offer */ 1389 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata); 1390 if (status != PJ_SUCCESS) { 1391 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status); 1392 return status; 1393 } 1394 1395 status = pjsip_inv_send_msg( call->inv, tdata); 1396 if (status != PJ_SUCCESS) { 1397 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status); 1398 return status; 1399 } 1400 1401 return PJ_SUCCESS; 1402 } 1403 1404 1405 /* 1406 * re-INVITE. 1407 */ 1408 PJ_DEF(pj_status_t) pjsua_call_reinvite(int call_index) 1409 { 1410 pjmedia_sdp_session *sdp; 1411 pjsip_tx_data *tdata; 1412 pjsua_call *call; 1413 pj_status_t status; 1414 1415 call = &pjsua.calls[call_index]; 1416 1417 if (!call->inv) { 1418 PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 1419 return PJSIP_ESESSIONTERMINATED; 1420 } 1421 1422 1423 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) { 1424 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed")); 1425 return PJSIP_ESESSIONSTATE; 1426 } 1427 1428 /* Create SDP */ 1429 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, call->inv->pool, 1, 1430 &call->skinfo, &sdp); 1431 if (status != PJ_SUCCESS) { 1432 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint", 1433 status); 1434 return status; 1435 } 1436 1437 /* Send re-INVITE with new offer */ 1438 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata); 1439 if (status != PJ_SUCCESS) { 1440 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status); 1441 return status; 1442 } 1443 1444 status = pjsip_inv_send_msg( call->inv, tdata); 1445 if (status != PJ_SUCCESS) { 1446 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status); 1447 return status; 1448 } 1449 1450 return PJ_SUCCESS; 1451 } 1452 1453 1454 /* 1455 * Transfer call. 1456 */ 1457 PJ_DEF(pj_status_t) pjsua_call_xfer(unsigned call_index, const pj_str_t *dest) 1458 { 1459 pjsip_evsub *sub; 1460 pjsip_tx_data *tdata; 1461 pjsua_call *call; 1462 pj_status_t status; 1463 1464 1465 call = &pjsua.calls[call_index]; 1466 1467 if (!call->inv) { 1468 PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 1469 return PJSIP_ESESSIONTERMINATED; 1470 } 1471 1472 /* Create xfer client subscription. 1473 * We're not interested in knowing the transfer result, so we 1474 * put NULL as the callback. 2158 } 2159 2160 /* Put the server subscription in inv_data. 2161 * Subsequent state changed in pjsua_inv_on_state_changed() will be 2162 * reported back to the server subscription. 1475 2163 */ 1476 status = pjsip_xfer_create_uac(call->inv->dlg, NULL, &sub); 1477 if (status != PJ_SUCCESS) { 1478 pjsua_perror(THIS_FILE, "Unable to create xfer", status); 1479 return status; 1480 } 1481 1482 /* 1483 * Create REFER request. 1484 */ 1485 status = pjsip_xfer_initiate(sub, dest, &tdata); 1486 if (status != PJ_SUCCESS) { 1487 pjsua_perror(THIS_FILE, "Unable to create REFER request", status); 1488 return status; 1489 } 1490 1491 /* Send. */ 1492 status = pjsip_xfer_send_request(sub, tdata); 1493 if (status != PJ_SUCCESS) { 1494 pjsua_perror(THIS_FILE, "Unable to send REFER request", status); 1495 return status; 1496 } 1497 1498 /* For simplicity (that's what this program is intended to be!), 1499 * leave the original invite session as it is. More advanced application 1500 * may want to hold the INVITE, or terminate the invite, or whatever. 1501 */ 1502 1503 return PJ_SUCCESS; 1504 } 1505 1506 1507 /** 1508 * Dial DTMF. 1509 */ 1510 PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( unsigned call_index, 1511 const pj_str_t *digits) 1512 { 1513 pjsua_call *call = &pjsua.calls[call_index]; 1514 1515 PJ_ASSERT_RETURN(call_index < pjsua.config.max_calls, PJ_EINVAL); 1516 1517 if (!call->session) { 1518 PJ_LOG(3,(THIS_FILE, "Media is not established yet!")); 1519 return -1; 1520 } 1521 1522 return pjmedia_session_dial_dtmf( call->session, 0, digits); 1523 } 1524 1525 1526 /** 1527 * Send instant messaging inside INVITE session. 1528 */ 1529 PJ_DEF(pj_status_t) pjsua_call_send_im(int call_index, const pj_str_t *str) 1530 { 1531 pjsua_call *call; 1532 const pj_str_t mime_text = pj_str("text"); 1533 const pj_str_t mime_plain = pj_str("plain"); 1534 pjsip_tx_data *tdata; 1535 pj_status_t status; 1536 1537 call = &pjsua.calls[call_index]; 1538 1539 if (!call->inv) { 1540 PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 1541 return PJSIP_ESESSIONTERMINATED; 1542 } 1543 1544 /* Lock dialog. */ 1545 pjsip_dlg_inc_lock(call->inv->dlg); 1546 1547 /* Create request message. */ 1548 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method, 1549 -1, &tdata); 1550 if (status != PJ_SUCCESS) { 1551 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status); 1552 goto on_return; 1553 } 1554 1555 /* Add accept header. */ 1556 pjsip_msg_add_hdr( tdata->msg, 1557 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool)); 1558 1559 /* Create "text/plain" message body. */ 1560 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &mime_text, 1561 &mime_plain, str); 1562 if (tdata->msg->body == NULL) { 1563 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM); 1564 pjsip_tx_data_dec_ref(tdata); 1565 goto on_return; 1566 } 1567 1568 /* Send the request. */ 1569 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL); 1570 if (status != PJ_SUCCESS) { 1571 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status); 1572 goto on_return; 1573 } 1574 1575 on_return: 1576 pjsip_dlg_dec_lock(call->inv->dlg); 1577 return status; 1578 } 1579 1580 1581 /** 1582 * Send IM typing indication inside INVITE session. 1583 */ 1584 PJ_DEF(pj_status_t) pjsua_call_send_typing_ind(int call_index, 1585 pj_bool_t is_typing) 1586 { 1587 pjsua_call *call; 1588 pjsip_tx_data *tdata; 1589 pj_status_t status; 1590 1591 call = &pjsua.calls[call_index]; 1592 1593 if (!call->inv) { 1594 PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 1595 return PJSIP_ESESSIONTERMINATED; 1596 } 1597 1598 /* Lock dialog. */ 1599 pjsip_dlg_inc_lock(call->inv->dlg); 1600 1601 /* Create request message. */ 1602 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method, 1603 -1, &tdata); 1604 if (status != PJ_SUCCESS) { 1605 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status); 1606 goto on_return; 1607 } 1608 1609 /* Create "application/im-iscomposing+xml" msg body. */ 1610 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing, 1611 NULL, NULL, -1); 1612 1613 /* Send the request. */ 1614 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL); 1615 if (status != PJ_SUCCESS) { 1616 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status); 1617 goto on_return; 1618 } 1619 1620 on_return: 1621 pjsip_dlg_dec_lock(call->inv->dlg); 1622 return status; 1623 } 1624 1625 1626 /* 1627 * Terminate all calls. 1628 */ 1629 PJ_DEF(void) pjsua_call_hangup_all(void) 1630 { 1631 unsigned i; 1632 1633 for (i=0; i<pjsua.config.max_calls; ++i) { 1634 pjsip_tx_data *tdata; 1635 int st_code; 1636 pjsua_call *call; 1637 1638 if (pjsua.calls[i].inv == NULL) 1639 continue; 1640 1641 call = &pjsua.calls[i]; 1642 1643 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED) { 1644 st_code = 200; 1645 } else { 1646 st_code = PJSIP_SC_GONE; 2164 pjsua_var.calls[new_call].xfer_sub = sub; 2165 2166 /* Put the invite_data in the subscription. */ 2167 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, 2168 &pjsua_var.calls[new_call]); 2169 } 2170 2171 2172 2173 /* 2174 * This callback is called when transaction state has changed in INVITE 2175 * session. We use this to trap: 2176 * - incoming REFER request. 2177 * - incoming MESSAGE request. 2178 */ 2179 static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv, 2180 pjsip_transaction *tsx, 2181 pjsip_event *e) 2182 { 2183 pjsua_call *call = inv->dlg->mod_data[pjsua_var.mod.id]; 2184 2185 PJSUA_LOCK(); 2186 2187 if (tsx->role==PJSIP_ROLE_UAS && 2188 tsx->state==PJSIP_TSX_STATE_TRYING && 2189 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0) 2190 { 2191 /* 2192 * Incoming REFER request. 2193 */ 2194 on_call_transfered(call->inv, e->body.tsx_state.src.rdata); 2195 2196 } 2197 else if (tsx->role==PJSIP_ROLE_UAS && 2198 tsx->state==PJSIP_TSX_STATE_TRYING && 2199 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0) 2200 { 2201 /* 2202 * Incoming MESSAGE request! 2203 */ 2204 pjsip_rx_data *rdata; 2205 pjsip_msg *msg; 2206 pjsip_accept_hdr *accept_hdr; 2207 pj_status_t status; 2208 2209 rdata = e->body.tsx_state.src.rdata; 2210 msg = rdata->msg_info.msg; 2211 2212 /* Request MUST have message body, with Content-Type equal to 2213 * "text/plain". 2214 */ 2215 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) { 2216 2217 pjsip_hdr hdr_list; 2218 2219 pj_list_init(&hdr_list); 2220 pj_list_push_back(&hdr_list, accept_hdr); 2221 2222 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE, 2223 NULL, &hdr_list, NULL ); 2224 PJSUA_UNLOCK(); 2225 return; 1647 2226 } 1648 2227 1649 if (pjsip_inv_end_session(call->inv, st_code, NULL, &tdata)==0) { 1650 if (tdata) 1651 pjsip_inv_send_msg(call->inv, tdata); 2228 /* Respond with 200 first, so that remote doesn't retransmit in case 2229 * the UI takes too long to process the message. 2230 */ 2231 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL); 2232 2233 /* Process MESSAGE request */ 2234 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str, 2235 &inv->dlg->local.info_str, rdata); 2236 2237 } 2238 else if (tsx->role == PJSIP_ROLE_UAC && 2239 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0) 2240 { 2241 /* Handle outgoing pager status */ 2242 if (tsx->status_code >= 200) { 2243 pjsua_im_data *im_data; 2244 2245 im_data = tsx->mod_data[pjsua_var.mod.id]; 2246 /* im_data can be NULL if this is typing indication */ 2247 2248 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) { 2249 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id, 2250 &im_data->to, 2251 &im_data->body, 2252 im_data->user_data, 2253 tsx->status_code, 2254 &tsx->status_text); 2255 } 1652 2256 } 1653 2257 } 1654 } 1655 1656 1657 pj_status_t pjsua_call_init(void) 1658 { 1659 /* Initialize invite session callback. */ 1660 pjsip_inv_callback inv_cb; 1661 pj_status_t status; 1662 1663 pj_memset(&inv_cb, 0, sizeof(inv_cb)); 1664 inv_cb.on_state_changed = &pjsua_call_on_state_changed; 1665 inv_cb.on_new_session = &pjsua_call_on_forked; 1666 inv_cb.on_media_update = &pjsua_call_on_media_update; 1667 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer; 1668 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed; 1669 1670 1671 /* Initialize invite session module: */ 1672 status = pjsip_inv_usage_init(pjsua.endpt, &inv_cb); 1673 1674 return status; 1675 } 1676 1677 /** 1678 * Replace media transport. 1679 */ 1680 PJ_DEF(pj_status_t) pjsua_set_call_media_transport( unsigned call_index, 1681 const pjmedia_sock_info *i, 1682 pjmedia_transport *tp) 1683 { 1684 pjsua_call *call = &pjsua.calls[call_index]; 1685 1686 if (i) 1687 pj_memcpy(&call->skinfo, i, sizeof(pjmedia_sock_info)); 1688 1689 if (call->med_tp) 1690 (*call->med_tp->op->destroy)(call->med_tp); 1691 1692 call->med_tp = tp; 1693 return PJ_SUCCESS; 1694 } 2258 2259 2260 PJSUA_UNLOCK(); 2261 } -
pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c
r492 r503 18 18 */ 19 19 #include <pjsua-lib/pjsua.h> 20 #include "pjsua_imp.h" 21 22 /* 23 * pjsua_core.c 20 #include <pjsua-lib/pjsua_internal.h> 21 22 23 #define THIS_FILE "pjsua_core.c" 24 25 26 /* PJSUA application instance. */ 27 struct pjsua_data pjsua_var; 28 29 30 /* Display error */ 31 PJ_DEF(void) pjsua_perror( const char *sender, const char *title, 32 pj_status_t status) 33 { 34 char errmsg[PJ_ERR_MSG_SIZE]; 35 36 pj_strerror(status, errmsg, sizeof(errmsg)); 37 PJ_LOG(3,(sender, "%s: %s [status=%d]", title, errmsg, status)); 38 } 39 40 41 static void init_data() 42 { 43 unsigned i; 44 45 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) 46 pjsua_var.acc[i].index = i; 47 48 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata); ++i) 49 pjsua_var.tpdata[i].index = i; 50 } 51 52 53 54 /***************************************************************************** 55 * This is a very simple PJSIP module, whose sole purpose is to display 56 * incoming and outgoing messages to log. This module will have priority 57 * higher than transport layer, which means: 24 58 * 25 * Core application functionalities. 26 */ 27 28 #define THIS_FILE "pjsua_core.c" 29 30 31 /* 32 * Global variable. 33 */ 34 struct pjsua pjsua; 35 36 37 /* 38 * Default local URI, if none is specified in cmd-line 39 */ 40 #define PJSUA_LOCAL_URI "<sip:user@127.0.0.1>" 41 42 43 44 /* 45 * Init default application parameters. 46 */ 47 PJ_DEF(void) pjsua_default_config(pjsua_config *cfg) 48 { 49 unsigned i; 50 51 pj_memset(cfg, 0, sizeof(pjsua_config)); 52 53 cfg->thread_cnt = 1; 54 cfg->media_has_ioqueue = 1; 55 cfg->media_thread_cnt = 1; 56 cfg->udp_port = 5060; 57 cfg->start_rtp_port = 4000; 58 cfg->msg_logging = PJ_TRUE; 59 cfg->max_calls = 4; 60 cfg->conf_ports = 0; 61 62 #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 63 pjsua.clock_rate = 44100; 64 #endif 65 66 cfg->complexity = 10; 67 cfg->quality = 10; 59 * - incoming messages will come to this module first before reaching 60 * transaction layer. 61 * 62 * - outgoing messages will come to this module last, after the message 63 * has been 'printed' to contiguous buffer by transport layer and 64 * appropriate transport instance has been decided for this message. 65 * 66 */ 67 68 /* Notification on incoming messages */ 69 static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata) 70 { 71 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%d:\n" 72 "%s\n" 73 "--end msg--", 74 rdata->msg_info.len, 75 pjsip_rx_data_get_info(rdata), 76 rdata->pkt_info.src_name, 77 rdata->pkt_info.src_port, 78 rdata->msg_info.msg_buf)); 68 79 69 cfg->auto_answer = 100;70 cfg->uas_duration = 3600;71 72 /* Default logging settings: */ 73 cfg->log_level = 5; 74 cfg->app_log_level = 4; 75 cfg->log_decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME | 76 PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE; 77 78 79 /* Also init logging settings in pjsua.config, because log80 * may be written before pjsua_init() is called.80 /* Always return false, otherwise messages will not get processed! */ 81 return PJ_FALSE; 82 } 83 84 /* Notification on outgoing messages */ 85 static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) 86 { 87 88 /* Important note: 89 * tp_info field is only valid after outgoing messages has passed 90 * transport layer. So don't try to access tp_info when the module 91 * has lower priority than transport layer. 81 92 */ 82 pjsua.config.log_level = 5; 83 pjsua.config.app_log_level = 4; 84 85 86 /* Init accounts: */ 87 for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) { 88 cfg->acc_config[i].reg_timeout = 55; 89 } 90 91 } 92 93 94 #define strncpy_with_null(dst,src,len) \ 95 do { \ 96 strncpy(dst, src, len); \ 97 dst[len-1] = '\0'; \ 98 } while (0) 99 100 101 102 PJ_DEF(pj_status_t) pjsua_test_config( const pjsua_config *cfg, 103 char *errmsg, 104 int len) 105 { 106 unsigned i; 107 108 /* If UDP port is zero, then sip_host and sip_port must be specified */ 109 if (cfg->udp_port == 0) { 110 if (cfg->sip_host.slen==0 || cfg->sip_port==0) { 111 strncpy_with_null(errmsg, 112 "sip_host and sip_port must be specified", 113 len); 114 return -1; 115 } 116 } 117 118 if (cfg->max_calls < 1) { 119 strncpy_with_null(errmsg, 120 "max_calls needs to be at least 1", 121 len); 122 return -1; 123 } 124 125 /* STUN */ 126 if (cfg->stun_srv1.slen || cfg->stun_port1 || cfg->stun_port2 || 127 cfg->stun_srv2.slen) 128 { 129 if (cfg->stun_port1 == 0) { 130 strncpy_with_null(errmsg, "stun_port1 required", len); 131 return -1; 132 } 133 if (cfg->stun_srv1.slen == 0) { 134 strncpy_with_null(errmsg, "stun_srv1 required", len); 135 return -1; 136 } 137 if (cfg->stun_port2 == 0) { 138 strncpy_with_null(errmsg, "stun_port2 required", len); 139 return -1; 140 } 141 if (cfg->stun_srv2.slen == 0) { 142 strncpy_with_null(errmsg, "stun_srv2 required", len); 143 return -1; 144 } 145 } 146 147 /* Verify accounts */ 148 for (i=0; i<cfg->acc_cnt; ++i) { 149 const pjsua_acc_config *acc_cfg = &cfg->acc_config[i]; 150 unsigned j; 151 152 if (acc_cfg->id.slen == 0) { 153 strncpy_with_null(errmsg, "missing account ID", len); 154 return -1; 155 } 156 157 if (acc_cfg->id.slen == 0) { 158 strncpy_with_null(errmsg, "missing registrar URI", len); 159 return -1; 160 } 161 162 if (acc_cfg->reg_timeout == 0) { 163 strncpy_with_null(errmsg, "missing registration timeout", len); 164 return -1; 165 } 166 167 168 for (j=0; j<acc_cfg->cred_count; ++j) { 169 170 if (acc_cfg->cred_info[j].scheme.slen == 0) { 171 strncpy_with_null(errmsg, "missing auth scheme in account", 172 len); 173 return -1; 174 } 175 176 if (acc_cfg->cred_info[j].realm.slen == 0) { 177 strncpy_with_null(errmsg, "missing realm in account", len); 178 return -1; 179 } 180 181 if (acc_cfg->cred_info[j].username.slen == 0) { 182 strncpy_with_null(errmsg, "missing username in account", len); 183 return -1; 184 } 185 186 } 187 } 188 93 94 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n" 95 "%s\n" 96 "--end msg--", 97 (tdata->buf.cur - tdata->buf.start), 98 pjsip_tx_data_get_info(tdata), 99 tdata->tp_info.dst_name, 100 tdata->tp_info.dst_port, 101 tdata->buf.start)); 102 103 /* Always return success, otherwise message will not get sent! */ 189 104 return PJ_SUCCESS; 190 105 } 191 106 107 /* The module instance. */ 108 static pjsip_module pjsua_msg_logger = 109 { 110 NULL, NULL, /* prev, next. */ 111 { "mod-pjsua-log", 13 }, /* Name. */ 112 -1, /* Id */ 113 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */ 114 NULL, /* load() */ 115 NULL, /* start() */ 116 NULL, /* stop() */ 117 NULL, /* unload() */ 118 &logging_on_rx_msg, /* on_rx_request() */ 119 &logging_on_rx_msg, /* on_rx_response() */ 120 &logging_on_tx_msg, /* on_tx_request. */ 121 &logging_on_tx_msg, /* on_tx_response() */ 122 NULL, /* on_tsx_state() */ 123 124 }; 125 126 127 /***************************************************************************** 128 * These two functions are the main callbacks registered to PJSIP stack 129 * to receive SIP request and response messages that are outside any 130 * dialogs and any transactions. 131 */ 192 132 193 133 /* … … 202 142 static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata) 203 143 { 144 pj_bool_t processed = PJ_FALSE; 145 146 PJSUA_LOCK(); 204 147 205 148 if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) { 206 149 207 return pjsua_call_on_incoming(rdata); 208 } 209 210 return PJ_FALSE; 150 processed = pjsua_call_on_incoming(rdata); 151 } 152 153 PJSUA_UNLOCK(); 154 155 return processed; 211 156 } 212 157 … … 230 175 231 176 232 static int PJ_THREAD_FUNC pjsua_poll(void *arg) 233 { 234 pj_status_t last_err = 0; 235 236 PJ_UNUSED_ARG(arg); 237 238 do { 239 pj_time_val timeout = { 0, 10 }; 240 pj_status_t status; 241 242 status = pjsip_endpt_handle_events (pjsua.endpt, &timeout); 243 if (status != PJ_SUCCESS && status != last_err) { 244 last_err = status; 245 pjsua_perror(THIS_FILE, "handle_events() returned error", status); 246 } 247 } while (!pjsua.quit_flag); 248 249 return 0; 250 } 251 252 /** 253 * Poll pjsua. 254 */ 255 PJ_DECL(int) pjsua_handle_events(unsigned msec_timeout) 256 { 257 unsigned count = 0; 258 pj_time_val tv; 177 /***************************************************************************** 178 * Logging. 179 */ 180 181 /* Log callback */ 182 static void log_writer(int level, const char *buffer, int len) 183 { 184 /* Write to stdout, file, and application callback. */ 185 186 if (level <= (int)pjsua_var.log_cfg.console_level) 187 pj_log_write(level, buffer, len); 188 189 if (pjsua_var.log_file) { 190 pj_ssize_t size = len; 191 pj_file_write(pjsua_var.log_file, buffer, &size); 192 } 193 194 if (pjsua_var.log_cfg.cb) 195 (*pjsua_var.log_cfg.cb)(level, buffer, len); 196 } 197 198 199 /* 200 * Application can call this function at any time (after pjsua_create(), of 201 * course) to change logging settings. 202 */ 203 PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg) 204 { 259 205 pj_status_t status; 260 206 261 tv.sec = 0; 262 tv.msec = msec_timeout; 263 pj_time_val_normalize(&tv); 264 265 status = pjsip_endpt_handle_events2(pjsua.endpt, &tv, &count); 266 if (status != PJ_SUCCESS) 267 return -status; 268 269 return count; 270 } 271 272 273 #define pjsua_has_stun() (pjsua.config.stun_port1 && \ 274 pjsua.config.stun_port2) 275 276 277 /* 278 * Create and initialize SIP socket (and possibly resolve public 279 * address via STUN, depending on config). 280 */ 281 static pj_status_t create_sip_udp_sock(int port, 282 pj_sock_t *p_sock, 283 pj_sockaddr_in *p_pub_addr) 284 { 285 pj_sock_t sock; 286 pj_status_t status; 287 288 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock); 289 if (status != PJ_SUCCESS) { 290 pjsua_perror(THIS_FILE, "socket() error", status); 291 return status; 292 } 293 294 status = pj_sock_bind_in(sock, 0, (pj_uint16_t)port); 295 if (status != PJ_SUCCESS) { 296 pjsua_perror(THIS_FILE, "bind() error", status); 297 pj_sock_close(sock); 298 return status; 299 } 300 301 if (pjsua_has_stun()) { 302 status = pj_stun_get_mapped_addr(&pjsua.cp.factory, 1, &sock, 303 &pjsua.config.stun_srv1, 304 pjsua.config.stun_port1, 305 &pjsua.config.stun_srv2, 306 pjsua.config.stun_port2, 307 p_pub_addr); 207 /* Save config. */ 208 pjsua_logging_config_dup(pjsua_var.pool, &pjsua_var.log_cfg, cfg); 209 210 /* Redirect log function to ours */ 211 pj_log_set_log_func( &log_writer ); 212 213 /* Close existing file, if any */ 214 if (pjsua_var.log_file) { 215 pj_file_close(pjsua_var.log_file); 216 pjsua_var.log_file = NULL; 217 } 218 219 /* If output log file is desired, create the file: */ 220 if (pjsua_var.log_cfg.log_filename.slen) { 221 222 status = pj_file_open(pjsua_var.pool, 223 pjsua_var.log_cfg.log_filename.ptr, 224 PJ_O_WRONLY, 225 &pjsua_var.log_file); 226 308 227 if (status != PJ_SUCCESS) { 309 pjsua_perror(THIS_FILE, "STUN resolve error", status); 310 pj_sock_close(sock); 228 pjsua_perror(THIS_FILE, "Error creating log file", status); 311 229 return status; 312 230 } 313 314 } else { 315 316 const pj_str_t *hostname = pj_gethostname(); 317 struct pj_hostent he; 318 319 status = pj_gethostbyname(hostname, &he); 320 if (status != PJ_SUCCESS) { 321 pjsua_perror(THIS_FILE, "Unable to resolve local host", status); 322 pj_sock_close(sock); 323 return status; 324 } 325 326 pj_memset(p_pub_addr, 0, sizeof(pj_sockaddr_in)); 327 p_pub_addr->sin_family = PJ_AF_INET; 328 p_pub_addr->sin_port = pj_htons((pj_uint16_t)port); 329 p_pub_addr->sin_addr = *(pj_in_addr*)he.h_addr; 330 } 331 332 *p_sock = sock; 231 } 232 233 /* Unregister msg logging if it's previously registered */ 234 if (pjsua_msg_logger.id >= 0) { 235 pjsip_endpt_unregister_module(pjsua_var.endpt, &pjsua_msg_logger); 236 pjsua_msg_logger.id = -1; 237 } 238 239 /* Enable SIP message logging */ 240 if (pjsua_var.log_cfg.msg_logging) 241 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_msg_logger); 242 243 333 244 return PJ_SUCCESS; 334 245 } 335 246 336 247 337 /* 338 * Create RTP and RTCP socket pair, and possibly resolve their public 339 * address via STUN. 340 */ 341 static pj_status_t create_rtp_rtcp_sock(pjmedia_sock_info *skinfo) 342 { 343 enum { 344 RTP_RETRY = 100 345 }; 346 int i; 347 static pj_uint16_t rtp_port; 348 pj_sockaddr_in mapped_addr[2]; 349 pj_status_t status = PJ_SUCCESS; 350 pj_sock_t sock[2]; 351 352 if (rtp_port == 0) 353 rtp_port = (pj_uint16_t)pjsua.config.start_rtp_port; 354 355 for (i=0; i<2; ++i) 356 sock[i] = PJ_INVALID_SOCKET; 357 358 359 /* Loop retry to bind RTP and RTCP sockets. */ 360 for (i=0; i<RTP_RETRY; ++i, rtp_port += 2) { 361 362 /* Create and bind RTP socket. */ 363 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[0]); 364 if (status != PJ_SUCCESS) { 365 pjsua_perror(THIS_FILE, "socket() error", status); 366 return status; 367 } 368 369 status = pj_sock_bind_in(sock[0], 0, rtp_port); 370 if (status != PJ_SUCCESS) { 371 pj_sock_close(sock[0]); 372 sock[0] = PJ_INVALID_SOCKET; 373 continue; 374 } 375 376 /* Create and bind RTCP socket. */ 377 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[1]); 378 if (status != PJ_SUCCESS) { 379 pjsua_perror(THIS_FILE, "socket() error", status); 380 pj_sock_close(sock[0]); 381 return status; 382 } 383 384 status = pj_sock_bind_in(sock[1], 0, (pj_uint16_t)(rtp_port+1)); 385 if (status != PJ_SUCCESS) { 386 pj_sock_close(sock[0]); 387 sock[0] = PJ_INVALID_SOCKET; 388 389 pj_sock_close(sock[1]); 390 sock[1] = PJ_INVALID_SOCKET; 391 continue; 392 } 393 394 /* 395 * If we're configured to use STUN, then find out the mapped address, 396 * and make sure that the mapped RTCP port is adjacent with the RTP. 397 */ 398 if (pjsua_has_stun()) { 399 status=pj_stun_get_mapped_addr(&pjsua.cp.factory, 2, sock, 400 &pjsua.config.stun_srv1, 401 pjsua.config.stun_port1, 402 &pjsua.config.stun_srv2, 403 pjsua.config.stun_port2, 404 mapped_addr); 405 if (status != PJ_SUCCESS) { 406 pjsua_perror(THIS_FILE, "STUN resolve error", status); 407 goto on_error; 408 } 409 410 if (pj_ntohs(mapped_addr[1].sin_port) == 411 pj_ntohs(mapped_addr[0].sin_port)+1) 412 { 413 /* Success! */ 414 break; 415 } 416 417 pj_sock_close(sock[0]); 418 sock[0] = PJ_INVALID_SOCKET; 419 420 pj_sock_close(sock[1]); 421 sock[1] = PJ_INVALID_SOCKET; 422 423 } else { 424 const pj_str_t *hostname; 425 pj_sockaddr_in addr; 426 427 /* Get local IP address. */ 428 hostname = pj_gethostname(); 429 430 pj_memset( &addr, 0, sizeof(addr)); 431 addr.sin_family = PJ_AF_INET; 432 status = pj_sockaddr_in_set_str_addr( &addr, hostname); 433 if (status != PJ_SUCCESS) { 434 pjsua_perror(THIS_FILE, "Unresolvable local hostname", 435 status); 436 goto on_error; 437 } 438 439 for (i=0; i<2; ++i) 440 pj_memcpy(&mapped_addr[i], &addr, sizeof(addr)); 441 442 mapped_addr[0].sin_port=pj_htons((pj_uint16_t)rtp_port); 443 mapped_addr[1].sin_port=pj_htons((pj_uint16_t)(rtp_port+1)); 444 break; 445 } 446 } 447 448 if (sock[0] == PJ_INVALID_SOCKET) { 449 PJ_LOG(1,(THIS_FILE, 450 "Unable to find appropriate RTP/RTCP ports combination")); 451 goto on_error; 452 } 453 454 455 skinfo->rtp_sock = sock[0]; 456 pj_memcpy(&skinfo->rtp_addr_name, 457 &mapped_addr[0], sizeof(pj_sockaddr_in)); 458 459 skinfo->rtcp_sock = sock[1]; 460 pj_memcpy(&skinfo->rtcp_addr_name, 461 &mapped_addr[1], sizeof(pj_sockaddr_in)); 462 463 PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s:%d", 464 pj_inet_ntoa(skinfo->rtp_addr_name.sin_addr), 465 pj_ntohs(skinfo->rtp_addr_name.sin_port))); 466 PJ_LOG(4,(THIS_FILE, "RTCP socket reachable at %s:%d", 467 pj_inet_ntoa(skinfo->rtcp_addr_name.sin_addr), 468 pj_ntohs(skinfo->rtcp_addr_name.sin_port))); 469 470 rtp_port += 2; 248 /***************************************************************************** 249 * PJSUA Base API. 250 */ 251 252 /* Worker thread function. */ 253 static int worker_thread(void *arg) 254 { 255 enum { TIMEOUT = 10 }; 256 257 PJ_UNUSED_ARG(arg); 258 259 while (!pjsua_var.thread_quit_flag) { 260 int count; 261 262 count = pjsua_handle_events(TIMEOUT); 263 if (count < 0) 264 pj_thread_sleep(TIMEOUT); 265 } 266 267 return 0; 268 } 269 270 271 /* 272 * Instantiate pjsua application. 273 */ 274 PJ_DEF(pj_status_t) pjsua_create(void) 275 { 276 pj_status_t status; 277 278 /* Init pjsua data */ 279 init_data(); 280 281 /* Set default logging settings */ 282 pjsua_logging_config_default(&pjsua_var.log_cfg); 283 284 /* Init PJLIB: */ 285 status = pj_init(); 286 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 287 288 289 /* Init PJLIB-UTIL: */ 290 status = pjlib_util_init(); 291 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 292 293 294 /* Init caching pool. */ 295 pj_caching_pool_init(&pjsua_var.cp, &pj_pool_factory_default_policy, 0); 296 297 /* Create memory pool for application. */ 298 pjsua_var.pool = pjsua_pool_create("pjsua", 4000, 4000); 299 300 PJ_ASSERT_RETURN(pjsua_var.pool, PJ_ENOMEM); 301 302 /* Create mutex */ 303 status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua", 304 &pjsua_var.mutex); 305 if (status != PJ_SUCCESS) { 306 pjsua_perror(THIS_FILE, "Unable to create mutex", status); 307 return status; 308 } 309 310 /* Must create SIP endpoint to initialize SIP parser. The parser 311 * is needed for example when application needs to call pjsua_verify_url(). 312 */ 313 status = pjsip_endpt_create(&pjsua_var.cp.factory, 314 pj_gethostname()->ptr, 315 &pjsua_var.endpt); 316 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 317 318 471 319 return PJ_SUCCESS; 472 473 on_error: 474 for (i=0; i<2; ++i) { 475 if (sock[i] != PJ_INVALID_SOCKET) 476 pj_sock_close(sock[i]); 477 } 478 return status; 479 } 480 481 482 483 /** 484 * Create pjsua application. 485 * This initializes pjlib/pjlib-util, and creates memory pool factory to 486 * be used by application. 487 */ 488 PJ_DEF(pj_status_t) pjsua_create(void) 489 { 320 } 321 322 323 /* 324 * Initialize pjsua with the specified settings. All the settings are 325 * optional, and the default values will be used when the config is not 326 * specified. 327 */ 328 PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, 329 const pjsua_logging_config *log_cfg, 330 const pjsua_media_config *media_cfg) 331 { 332 pjsua_config default_cfg; 333 pjsua_media_config default_media_cfg; 490 334 pj_status_t status; 491 335 492 /* Init PJLIB: */ 493 494 status = pj_init(); 495 if (status != PJ_SUCCESS) { 496 pjsua_perror(THIS_FILE, "pj_init() error", status); 497 return status; 498 } 499 500 /* Init PJLIB-UTIL: */ 501 502 status = pjlib_util_init(); 503 if (status != PJ_SUCCESS) { 504 pjsua_perror(THIS_FILE, "pjlib_util_init() error", status); 505 return status; 506 } 507 508 /* Init memory pool: */ 509 510 /* Init caching pool. */ 511 pj_caching_pool_init(&pjsua.cp, &pj_pool_factory_default_policy, 0); 512 513 /* Create memory pool for application. */ 514 pjsua.pool = pj_pool_create(&pjsua.cp.factory, "pjsua", 4000, 4000, NULL); 515 516 /* Must create endpoint to initialize SIP parser. */ 517 /* Create global endpoint: */ 518 519 status = pjsip_endpt_create(&pjsua.cp.factory, 520 pj_gethostname()->ptr, 521 &pjsua.endpt); 522 if (status != PJ_SUCCESS) { 523 pjsua_perror(THIS_FILE, "Unable to create SIP endpoint", status); 524 return status; 525 } 526 527 /* Must create media endpoint too */ 528 status = pjmedia_endpt_create(&pjsua.cp.factory, 529 pjsua.config.media_has_ioqueue? NULL : 530 pjsip_endpt_get_ioqueue(pjsua.endpt), 531 pjsua.config.media_thread_cnt, 532 &pjsua.med_endpt); 533 if (status != PJ_SUCCESS) { 534 pjsua_perror(THIS_FILE, 535 "Media stack initialization has returned error", 536 status); 537 return status; 538 } 539 540 541 return PJ_SUCCESS; 542 } 543 544 545 546 /* 547 * Init media. 548 */ 549 static pj_status_t init_media(void) 550 { 551 int i; 552 unsigned options; 553 pj_str_t codec_id; 554 pj_status_t status; 555 556 /* Register all codecs */ 557 #if PJMEDIA_HAS_SPEEX_CODEC 558 /* Register speex. */ 559 status = pjmedia_codec_speex_init(pjsua.med_endpt, 560 PJMEDIA_SPEEX_NO_UWB, 561 pjsua.config.quality, 562 pjsua.config.complexity ); 563 if (status != PJ_SUCCESS) { 564 pjsua_perror(THIS_FILE, "Error initializing Speex codec", 565 status); 566 return status; 567 } 568 569 /* Set "speex/16000/1" to have highest priority */ 570 codec_id = pj_str("speex/16000/1"); 571 pjmedia_codec_mgr_set_codec_priority( 572 pjmedia_endpt_get_codec_mgr(pjsua.med_endpt), 573 &codec_id, 574 PJMEDIA_CODEC_PRIO_HIGHEST); 575 576 #endif /* PJMEDIA_HAS_SPEEX_CODEC */ 577 578 #if PJMEDIA_HAS_GSM_CODEC 579 /* Register GSM */ 580 status = pjmedia_codec_gsm_init(pjsua.med_endpt); 581 if (status != PJ_SUCCESS) { 582 pjsua_perror(THIS_FILE, "Error initializing GSM codec", 583 status); 584 return status; 585 } 586 #endif /* PJMEDIA_HAS_GSM_CODEC */ 587 588 #if PJMEDIA_HAS_G711_CODEC 589 /* Register PCMA and PCMU */ 590 status = pjmedia_codec_g711_init(pjsua.med_endpt); 591 if (status != PJ_SUCCESS) { 592 pjsua_perror(THIS_FILE, "Error initializing G711 codec", 593 status); 594 return status; 595 } 596 #endif /* PJMEDIA_HAS_G711_CODEC */ 597 598 #if PJMEDIA_HAS_L16_CODEC 599 /* Register L16 family codecs, but disable all */ 600 status = pjmedia_codec_l16_init(pjsua.med_endpt, 0); 601 if (status != PJ_SUCCESS) { 602 pjsua_perror(THIS_FILE, "Error initializing L16 codecs", 603 status); 604 return status; 605 } 606 607 /* Disable ALL L16 codecs */ 608 codec_id = pj_str("L16"); 609 pjmedia_codec_mgr_set_codec_priority( 610 pjmedia_endpt_get_codec_mgr(pjsua.med_endpt), 611 &codec_id, 612 PJMEDIA_CODEC_PRIO_DISABLED); 613 614 #endif /* PJMEDIA_HAS_L16_CODEC */ 615 616 617 /* Enable those codecs that user put with "--add-codec", and move 618 * the priority to top 619 */ 620 for (i=0; i<(int)pjsua.config.codec_cnt; ++i) { 621 pjmedia_codec_mgr_set_codec_priority( 622 pjmedia_endpt_get_codec_mgr(pjsua.med_endpt), 623 &pjsua.config.codec_arg[i], 624 PJMEDIA_CODEC_PRIO_HIGHEST); 625 } 626 627 628 /* Init options for conference bridge. */ 629 options = PJMEDIA_CONF_NO_DEVICE; 630 631 /* Calculate maximum number of ports, if it's not specified */ 632 if (pjsua.config.conf_ports == 0) { 633 pjsua.config.conf_ports = 3 * pjsua.config.max_calls; 634 } 635 636 /* Init conference bridge. */ 637 pjsua.clock_rate = pjsua.config.clock_rate ? pjsua.config.clock_rate : 16000; 638 pjsua.samples_per_frame = pjsua.clock_rate * 10 / 1000; 639 status = pjmedia_conf_create(pjsua.pool, 640 pjsua.config.conf_ports, 641 pjsua.clock_rate, 642 1, /* mono */ 643 pjsua.samples_per_frame, 644 16, 645 options, 646 &pjsua.mconf); 647 if (status != PJ_SUCCESS) { 648 pjsua_perror(THIS_FILE, 649 "Media stack initialization has returned error", 650 status); 651 return status; 652 } 653 654 if (pjsua.config.null_audio == PJ_FALSE) { 655 pjmedia_port *conf_port; 656 657 /* Create sound device port */ 658 status = pjmedia_snd_port_create(pjsua.pool, 659 pjsua.config.snd_capture_id, 660 pjsua.config.snd_player_id, 661 pjsua.clock_rate, 1 /* mono */, 662 pjsua.samples_per_frame, 16, 663 0, &pjsua.snd_port); 664 if (status != PJ_SUCCESS) { 665 pjsua_perror(THIS_FILE, "Unable to create sound device", status); 666 return status; 667 } 668 669 /* Get the port interface of the conference bridge */ 670 conf_port = pjmedia_conf_get_master_port(pjsua.mconf); 671 672 /* Connect conference port interface to sound port */ 673 pjmedia_snd_port_connect( pjsua.snd_port, conf_port); 674 675 } else { 676 pjmedia_port *null_port, *conf_port; 677 678 /* Create NULL port */ 679 status = pjmedia_null_port_create(pjsua.pool, pjsua.clock_rate, 680 1, pjsua.samples_per_frame, 16, 681 &null_port); 682 if (status != PJ_SUCCESS) { 683 pjsua_perror(THIS_FILE, "Unable to create NULL port", status); 684 return status; 685 } 686 687 /* Get the port interface of the conference bridge */ 688 conf_port = pjmedia_conf_get_master_port(pjsua.mconf); 689 690 /* Create master port to control conference bridge's clock */ 691 status = pjmedia_master_port_create(pjsua.pool, null_port, conf_port, 692 0, &pjsua.master_port); 693 if (status != PJ_SUCCESS) { 694 pjsua_perror(THIS_FILE, "Unable to create master port", status); 695 return status; 696 } 697 } 698 699 /* Create WAV file player if required: */ 700 701 if (pjsua.config.wav_file.slen) { 702 703 status = pjsua_player_create(&pjsua.config.wav_file, NULL); 704 if (status != PJ_SUCCESS) { 705 pjsua_perror(THIS_FILE, "Unable to create file player", 706 status); 707 return status; 708 } 709 } 710 711 712 return PJ_SUCCESS; 713 } 714 715 716 /* 717 * Copy account configuration. 718 */ 719 static void copy_acc_config(pj_pool_t *pool, 720 pjsua_acc_config *dst_acc, 721 const pjsua_acc_config *src_acc) 722 { 723 unsigned j; 724 725 pj_memcpy(dst_acc, src_acc, sizeof(pjsua_acc_config)); 726 727 pj_strdup_with_null(pool, &dst_acc->id, &src_acc->id); 728 pj_strdup_with_null(pool, &dst_acc->reg_uri, &src_acc->reg_uri); 729 pj_strdup_with_null(pool, &dst_acc->contact, &src_acc->contact); 730 pj_strdup_with_null(pool, &dst_acc->proxy, &src_acc->proxy); 731 732 for (j=0; j<src_acc->cred_count; ++j) { 733 pj_strdup_with_null(pool, &dst_acc->cred_info[j].realm, 734 &src_acc->cred_info[j].realm); 735 pj_strdup_with_null(pool, &dst_acc->cred_info[j].scheme, 736 &src_acc->cred_info[j].scheme); 737 pj_strdup_with_null(pool, &dst_acc->cred_info[j].username, 738 &src_acc->cred_info[j].username); 739 pj_strdup_with_null(pool, &dst_acc->cred_info[j].data, 740 &src_acc->cred_info[j].data); 741 } 742 } 743 744 745 /* 746 * Copy configuration. 747 */ 748 void pjsua_copy_config( pj_pool_t *pool, pjsua_config *dst, 749 const pjsua_config *src) 750 { 751 unsigned i; 752 753 /* Plain memcpy */ 754 pj_memcpy(dst, src, sizeof(pjsua_config)); 755 756 /* Duplicate strings */ 757 pj_strdup_with_null(pool, &dst->sip_host, &src->sip_host); 758 pj_strdup_with_null(pool, &dst->stun_srv1, &src->stun_srv1); 759 pj_strdup_with_null(pool, &dst->stun_srv2, &src->stun_srv2); 760 pj_strdup_with_null(pool, &dst->wav_file, &src->wav_file); 761 762 for (i=0; i<src->codec_cnt; ++i) { 763 pj_strdup_with_null(pool, &dst->codec_arg[i], &src->codec_arg[i]); 764 } 765 766 pj_strdup_with_null(pool, &dst->outbound_proxy, &src->outbound_proxy); 767 //pj_strdup_with_null(pool, &dst->uri_to_call, &src->uri_to_call); 768 769 for (i=0; i<src->acc_cnt; ++i) { 770 pjsua_acc_config *dst_acc = &dst->acc_config[i]; 771 const pjsua_acc_config *src_acc = &src->acc_config[i]; 772 copy_acc_config(pool, dst_acc, src_acc); 773 } 774 775 pj_strdup_with_null(pool, &dst->log_filename, &src->log_filename); 776 777 for (i=0; i<src->buddy_cnt; ++i) { 778 pj_strdup_with_null(pool, &dst->buddy_uri[i], &src->buddy_uri[i]); 779 } 780 } 781 782 783 /***************************************************************************** 784 * Console application custom logging: 785 */ 786 787 788 static void log_writer(int level, const char *buffer, int len) 789 { 790 /* Write to both stdout and file. */ 791 792 if (level <= (int)pjsua.config.app_log_level) 793 pj_log_write(level, buffer, len); 794 795 if (pjsua.log_file) { 796 fwrite(buffer, len, 1, pjsua.log_file); 797 fflush(pjsua.log_file); 798 } 799 } 800 801 802 static pj_status_t logging_init() 803 { 804 /* Redirect log function to ours */ 805 806 pj_log_set_log_func( &log_writer ); 807 808 /* If output log file is desired, create the file: */ 809 810 if (pjsua.config.log_filename.slen) { 811 pjsua.log_file = fopen(pjsua.config.log_filename.ptr, "wt"); 812 if (pjsua.log_file == NULL) { 813 PJ_LOG(1,(THIS_FILE, "Unable to open log file %s", 814 pjsua.config.log_filename.ptr)); 815 return -1; 816 } 817 } 818 819 /* Enable SIP message logging */ 820 if (pjsua.config.msg_logging) 821 pjsip_endpt_register_module(pjsua.endpt, &pjsua_msg_logger); 822 823 return PJ_SUCCESS; 824 } 825 826 827 static void logging_shutdown(void) 828 { 829 /* Close logging file, if any: */ 830 831 if (pjsua.log_file) { 832 fclose(pjsua.log_file); 833 pjsua.log_file = NULL; 834 } 835 } 836 837 838 /* 839 * Initialize pjsua application. 840 * This will initialize all libraries, create endpoint instance, and register 841 * pjsip modules. 842 */ 843 PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg, 844 const pjsua_callback *cb) 845 { 846 char errmsg[80]; 847 unsigned i; 848 pj_status_t status; 849 850 851 /* Init accounts: */ 852 for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) { 853 pjsua.acc[i].index = i; 854 pjsua.acc[i].online_status = PJ_TRUE; 855 pj_list_init(&pjsua.acc[i].route_set); 856 pj_list_init(&pjsua.acc[i].pres_srv_list); 857 } 858 859 /* Init call array: */ 860 for (i=0; i<PJ_ARRAY_SIZE(pjsua.calls); ++i) { 861 pjsua.calls[i].index = i; 862 pjsua.calls[i].refresh_tm._timer_id = -1; 863 pjsua.calls[i].hangup_tm._timer_id = -1; 864 pjsua.calls[i].conf_slot = 0; 865 } 866 867 /* Init buddies array */ 868 for (i=0; i<PJ_ARRAY_SIZE(pjsua.buddies); ++i) { 869 pjsua.buddies[i].index = i; 870 } 871 872 /* Copy configuration */ 873 pjsua_copy_config(pjsua.pool, &pjsua.config, cfg); 874 875 /* Copy callback */ 876 pj_memcpy(&pjsua.cb, cb, sizeof(pjsua_callback)); 877 878 /* Test configuration */ 879 if (pjsua_test_config(&pjsua.config, errmsg, sizeof(errmsg))) { 880 PJ_LOG(1,(THIS_FILE, "Error in configuration: %s", errmsg)); 881 status = -1; 882 goto on_error; 883 } 884 885 886 /* Init PJLIB logging: */ 887 888 pj_log_set_level(pjsua.config.log_level); 889 pj_log_set_decor(pjsua.config.log_decor); 890 891 status = logging_init(); 892 if (status != PJ_SUCCESS) 893 goto on_error; 894 895 896 /* Create SIP UDP socket */ 897 if (pjsua.config.udp_port) { 898 899 status = create_sip_udp_sock( pjsua.config.udp_port, 900 &pjsua.sip_sock, 901 &pjsua.sip_sock_name); 902 if (status != PJ_SUCCESS) 903 goto on_error; 904 905 pj_strdup2_with_null(pjsua.pool, &pjsua.config.sip_host, 906 pj_inet_ntoa(pjsua.sip_sock_name.sin_addr)); 907 pjsua.config.sip_port = pj_ntohs(pjsua.sip_sock_name.sin_port); 908 909 } else { 910 911 /* Check that SIP host and port is configured */ 912 if (cfg->sip_host.slen == 0 || cfg->sip_port == 0) { 913 PJ_LOG(1,(THIS_FILE, 914 "Error: sip_host and sip_port must be specified")); 915 status = PJ_EINVAL; 916 goto on_error; 917 } 918 919 pjsua.sip_sock = PJ_INVALID_SOCKET; 920 } 921 922 923 /* Init media endpoint */ 924 status = init_media(); 925 if (status != PJ_SUCCESS) 926 goto on_error; 927 928 929 /* Init RTP sockets, only when UDP transport is enabled */ 930 for (i=0; pjsua.config.start_rtp_port && i<pjsua.config.max_calls; ++i) { 931 status = create_rtp_rtcp_sock(&pjsua.calls[i].skinfo); 932 if (status != PJ_SUCCESS) { 933 unsigned j; 934 for (j=0; j<i; ++j) { 935 if (pjsua.calls[i].med_tp) 936 pjsua.calls[i].med_tp->op->destroy(pjsua.calls[i].med_tp); 937 } 938 goto on_error; 939 } 940 status = pjmedia_transport_udp_attach(pjsua.med_endpt, NULL, 941 &pjsua.calls[i].skinfo, 0, 942 &pjsua.calls[i].med_tp); 943 } 944 945 /* Init PJSIP : */ 336 337 /* Create default configurations when the config is not supplied */ 338 339 if (ua_cfg == NULL) { 340 pjsua_config_default(&default_cfg); 341 ua_cfg = &default_cfg; 342 } 343 344 if (media_cfg == NULL) { 345 pjsua_media_config_default(&default_media_cfg); 346 media_cfg = &default_media_cfg; 347 } 348 349 /* Initialize logging first so that info/errors can be captured */ 350 if (log_cfg) { 351 status = pjsua_reconfigure_logging(log_cfg); 352 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 353 } 354 355 /* Init SIP UA: */ 946 356 947 357 /* Initialize transaction layer: */ 948 949 status = pjsip_tsx_layer_init_module(pjsua.endpt); 950 if (status != PJ_SUCCESS) { 951 pjsua_perror(THIS_FILE, "Transaction layer initialization error", 952 status); 953 goto on_error; 954 } 358 status = pjsip_tsx_layer_init_module(pjsua_var.endpt); 359 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 360 955 361 956 362 /* Initialize UA layer module: */ 957 958 status = pjsip_ua_init_module( pjsua.endpt, NULL ); 959 if (status != PJ_SUCCESS) { 960 pjsua_perror(THIS_FILE, "UA layer initialization error", status); 961 goto on_error; 962 } 963 964 /* Initialize and register pjsua's application module: */ 965 363 status = pjsip_ua_init_module( pjsua_var.endpt, NULL ); 364 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 365 366 367 /* Initialize and register PJSUA application module. */ 966 368 { 967 pjsip_module my_mod=369 const pjsip_module mod_initializer = 968 370 { 969 371 NULL, NULL, /* prev, next. */ … … 982 384 }; 983 385 984 pjsua.mod = my_mod; 985 986 status = pjsip_endpt_register_module(pjsua.endpt, &pjsua.mod); 987 if (status != PJ_SUCCESS) { 988 pjsua_perror(THIS_FILE, "Unable to register pjsua module", 989 status); 990 goto on_error; 386 pjsua_var.mod = mod_initializer; 387 388 status = pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_var.mod); 389 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 390 } 391 392 393 394 /* Initialize PJSUA call subsystem: */ 395 status = pjsua_call_subsys_init(ua_cfg); 396 if (status != PJ_SUCCESS) 397 goto on_error; 398 399 400 /* Initialize PJSUA media subsystem */ 401 status = pjsua_media_subsys_init(media_cfg); 402 if (status != PJ_SUCCESS) 403 goto on_error; 404 405 406 /* Init core SIMPLE module : */ 407 status = pjsip_evsub_init_module(pjsua_var.endpt); 408 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 409 410 411 /* Init presence module: */ 412 status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance()); 413 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 414 415 416 /* Init xfer/REFER module */ 417 status = pjsip_xfer_init_module( pjsua_var.endpt ); 418 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 419 420 /* Init pjsua presence handler: */ 421 status = pjsua_pres_init(); 422 if (status != PJ_SUCCESS) 423 goto on_error; 424 425 /* Init out-of-dialog MESSAGE request handler. */ 426 status = pjsua_im_init(); 427 if (status != PJ_SUCCESS) 428 goto on_error; 429 430 /* Start worker thread if needed. */ 431 if (pjsua_var.ua_cfg.thread_cnt) { 432 unsigned i; 433 434 if (pjsua_var.ua_cfg.thread_cnt > PJ_ARRAY_SIZE(pjsua_var.thread)) 435 pjsua_var.ua_cfg.thread_cnt = PJ_ARRAY_SIZE(pjsua_var.thread); 436 437 for (i=0; i<pjsua_var.ua_cfg.thread_cnt; ++i) { 438 status = pj_thread_create(pjsua_var.pool, "pjsua", &worker_thread, 439 NULL, 0, 0, &pjsua_var.thread[i]); 440 if (status != PJ_SUCCESS) 441 goto on_error; 991 442 } 992 443 } 993 444 994 /* Initialize invite session module: */ 995 996 status = pjsua_call_init(); 997 if (status != PJ_SUCCESS) { 998 pjsua_perror(THIS_FILE, "Invite usage initialization error", 999 status); 1000 goto on_error; 1001 } 1002 1003 /* Init core SIMPLE module : */ 1004 1005 pjsip_evsub_init_module(pjsua.endpt); 1006 1007 /* Init presence module: */ 1008 1009 pjsip_pres_init_module( pjsua.endpt, pjsip_evsub_instance()); 1010 1011 /* Init xfer/REFER module */ 1012 1013 pjsip_xfer_init_module( pjsua.endpt ); 1014 1015 /* Init pjsua presence handler: */ 1016 1017 pjsua_pres_init(); 1018 1019 /* Init out-of-dialog MESSAGE request handler. */ 1020 1021 pjsua_im_init(); 1022 1023 1024 /* Done. */ 445 /* Done! */ 446 447 PJ_LOG(3,(THIS_FILE, "pjsua version %s for %s initialized", 448 PJ_VERSION, PJ_OS_NAME)); 449 1025 450 return PJ_SUCCESS; 1026 451 … … 1031 456 1032 457 1033 /*1034 * Find account for incoming request.1035 */1036 PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_incoming(pjsip_rx_data *rdata)1037 {1038 pjsip_uri *uri;1039 pjsip_sip_uri *sip_uri;1040 unsigned acc_index;1041 1042 uri = rdata->msg_info.to->uri;1043 1044 /* Just return default account if To URI is not SIP: */1045 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&1046 !PJSIP_URI_SCHEME_IS_SIPS(uri))1047 {1048 return pjsua.default_acc;1049 }1050 1051 1052 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);1053 1054 /* Find account which has matching username and domain. */1055 for (acc_index=0; acc_index < pjsua.config.acc_cnt; ++acc_index) {1056 1057 pjsua_acc *acc = &pjsua.acc[acc_index];1058 1059 if (pj_stricmp(&acc->user_part, &sip_uri->user)==0 &&1060 pj_stricmp(&acc->host_part, &sip_uri->host)==0)1061 {1062 /* Match ! */1063 return acc_index;1064 }1065 }1066 1067 /* No matching, try match domain part only. */1068 for (acc_index=0; acc_index < pjsua.config.acc_cnt; ++acc_index) {1069 1070 pjsua_acc *acc = &pjsua.acc[acc_index];1071 1072 if (pj_stricmp(&acc->host_part, &sip_uri->host)==0) {1073 /* Match ! */1074 return acc_index;1075 }1076 }1077 1078 /* Still no match, use default account */1079 return pjsua.default_acc;1080 }1081 1082 1083 /*1084 * Find account for outgoing request.1085 */1086 PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_outgoing(const pj_str_t *str_url)1087 {1088 pj_str_t tmp;1089 pjsip_uri *uri;1090 pjsip_sip_uri *sip_uri;1091 unsigned i;1092 1093 pj_strdup_with_null(pjsua.pool, &tmp, str_url);1094 1095 uri = pjsip_parse_uri(pjsua.pool, tmp.ptr, tmp.slen, 0);1096 if (!uri)1097 return pjsua.config.acc_cnt-1;1098 1099 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&1100 !PJSIP_URI_SCHEME_IS_SIPS(uri))1101 {1102 /* Return the first account with proxy */1103 for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) {1104 if (!pjsua.acc[i].valid)1105 continue;1106 if (pjsua.config.acc_config[i].proxy.slen)1107 break;1108 }1109 1110 if (i != PJ_ARRAY_SIZE(pjsua.acc))1111 return i;1112 1113 /* Not found, use default account */1114 return pjsua.default_acc;1115 }1116 1117 sip_uri = pjsip_uri_get_uri(uri);1118 1119 /* Find matching domain */1120 for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) {1121 if (!pjsua.acc[i].valid)1122 continue;1123 if (pj_stricmp(&pjsua.acc[i].host_part, &sip_uri->host)==0)1124 break;1125 }1126 1127 if (i != PJ_ARRAY_SIZE(pjsua.acc))1128 return i;1129 1130 /* Just use default account */1131 return pjsua.default_acc;1132 }1133 1134 1135 /*1136 * Init account1137 */1138 static pj_status_t init_acc(unsigned acc_index)1139 {1140 pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[acc_index];1141 pjsua_acc *acc = &pjsua.acc[acc_index];1142 pjsip_uri *uri;1143 pjsip_sip_uri *sip_uri;1144 1145 /* Need to parse local_uri to get the elements: */1146 1147 uri = pjsip_parse_uri(pjsua.pool, acc_cfg->id.ptr,1148 acc_cfg->id.slen, 0);1149 if (uri == NULL) {1150 pjsua_perror(THIS_FILE, "Invalid local URI",1151 PJSIP_EINVALIDURI);1152 return PJSIP_EINVALIDURI;1153 }1154 1155 /* Local URI MUST be a SIP or SIPS: */1156 1157 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&1158 !PJSIP_URI_SCHEME_IS_SIPS(uri))1159 {1160 pjsua_perror(THIS_FILE, "Invalid local URI",1161 PJSIP_EINVALIDSCHEME);1162 return PJSIP_EINVALIDSCHEME;1163 }1164 1165 1166 /* Get the SIP URI object: */1167 1168 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);1169 1170 acc->user_part = sip_uri->user;1171 acc->host_part = sip_uri->host;1172 1173 /* Build Contact header */1174 1175 if (acc_cfg->contact.slen == 0) {1176 char contact[128];1177 const char *addr;1178 int port;1179 int len;1180 1181 addr = pjsua.config.sip_host.ptr;1182 port = pjsua.config.sip_port;1183 1184 /* The local Contact is the username@ip-addr, where1185 * - username is taken from the local URI,1186 * - ip-addr in UDP transport's address name (which may have been1187 * resolved from STUN.1188 */1189 1190 /* Build temporary contact string. */1191 1192 if (sip_uri->user.slen) {1193 1194 /* With the user part. */1195 len = pj_ansi_snprintf(contact, sizeof(contact),1196 "<sip:%.*s@%s:%d>",1197 (int)sip_uri->user.slen,1198 sip_uri->user.ptr,1199 addr, port);1200 } else {1201 1202 /* Without user part */1203 1204 len = pj_ansi_snprintf(contact, sizeof(contact),1205 "<sip:%s:%d>",1206 addr, port);1207 }1208 1209 if (len < 1 || len >= sizeof(contact)) {1210 pjsua_perror(THIS_FILE, "Invalid Contact", PJSIP_EURITOOLONG);1211 return PJSIP_EURITOOLONG;1212 }1213 1214 /* Duplicate Contact uri. */1215 1216 pj_strdup2(pjsua.pool, &acc_cfg->contact, contact);1217 1218 }1219 1220 1221 /* Build route-set for this account */1222 if (pjsua.config.outbound_proxy.slen) {1223 pj_str_t hname = { "Route", 5};1224 pjsip_route_hdr *r;1225 pj_str_t tmp;1226 1227 pj_strdup_with_null(pjsua.pool, &tmp, &pjsua.config.outbound_proxy);1228 r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL);1229 pj_list_push_back(&acc->route_set, r);1230 }1231 1232 if (acc_cfg->proxy.slen) {1233 pj_str_t hname = { "Route", 5};1234 pjsip_route_hdr *r;1235 pj_str_t tmp;1236 1237 pj_strdup_with_null(pjsua.pool, &tmp, &acc_cfg->proxy);1238 r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL);1239 pj_list_push_back(&acc->route_set, r);1240 }1241 1242 /* Mark account as valid */1243 pjsua.acc[acc_index].valid = PJ_TRUE;1244 1245 1246 return PJ_SUCCESS;1247 }1248 1249 /*1250 * Add a new account.1251 */1252 PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg,1253 pjsua_acc_id *acc_index)1254 {1255 unsigned index;1256 pj_status_t status;1257 1258 PJ_ASSERT_RETURN(pjsua.config.acc_cnt <1259 PJ_ARRAY_SIZE(pjsua.config.acc_config),1260 PJ_ETOOMANY);1261 1262 /* Find empty account index. */1263 for (index=0; index < PJ_ARRAY_SIZE(pjsua.acc); ++index) {1264 if (pjsua.acc[index].valid == PJ_FALSE)1265 break;1266 }1267 1268 /* Expect to find a slot */1269 PJ_ASSERT_RETURN(index < PJ_ARRAY_SIZE(pjsua.acc), PJ_EBUG);1270 1271 copy_acc_config(pjsua.pool, &pjsua.config.acc_config[index], cfg);1272 1273 status = init_acc(index);1274 if (status != PJ_SUCCESS) {1275 pjsua_perror(THIS_FILE, "Error adding account", status);1276 return status;1277 }1278 1279 if (acc_index)1280 *acc_index = index;1281 1282 pjsua.config.acc_cnt++;1283 1284 return PJ_SUCCESS;1285 }1286 1287 1288 /*1289 * Delete account.1290 */1291 PJ_DEF(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_index)1292 {1293 PJ_ASSERT_RETURN(acc_index < (int)pjsua.config.acc_cnt,1294 PJ_EINVAL);1295 PJ_ASSERT_RETURN(pjsua.acc[acc_index].valid, PJ_EINVALIDOP);1296 1297 /* Delete registration */1298 if (pjsua.acc[acc_index].regc != NULL)1299 pjsua_acc_set_registration(acc_index, PJ_FALSE);1300 1301 /* Invalidate */1302 pjsua.acc[acc_index].valid = PJ_FALSE;1303 1304 return PJ_SUCCESS;1305 }1306 1307 1308 /*1309 * Start pjsua stack.1310 * This will start the registration process, if registration is configured.1311 */1312 PJ_DEF(pj_status_t) pjsua_start(void)1313 {1314 int i; /* Must be signed */1315 unsigned count;1316 pj_status_t status = PJ_SUCCESS;1317 1318 1319 /* Add UDP transport: */1320 if (pjsua.sip_sock > 0) {1321 1322 /* Init the published name for the transport.1323 * Depending whether STUN is used, this may be the STUN mapped1324 * address, or socket's bound address.1325 */1326 pjsip_host_port addr_name;1327 1328 addr_name.host = pjsua.config.sip_host;1329 addr_name.port = pjsua.config.sip_port;1330 1331 /* Create UDP transport from previously created UDP socket: */1332 1333 status = pjsip_udp_transport_attach( pjsua.endpt, pjsua.sip_sock,1334 &addr_name, 1,1335 NULL);1336 if (status != PJ_SUCCESS) {1337 pjsua_perror(THIS_FILE, "Unable to start UDP transport",1338 status);1339 goto on_error;1340 }1341 }1342 1343 /* Initialize all unused accounts with default id and contact.1344 */1345 {1346 char buf[80];1347 pj_str_t tmp, id;1348 1349 tmp.ptr = buf;1350 tmp.slen = pj_ansi_sprintf(tmp.ptr, "Local <sip:%s:%d>",1351 pjsua.config.sip_host.ptr,1352 pjsua.config.sip_port);1353 pj_strdup_with_null( pjsua.pool, &id, &tmp);1354 1355 for (i=pjsua.config.acc_cnt; i<PJ_ARRAY_SIZE(pjsua.config.acc_config);1356 ++i)1357 {1358 pjsua_acc_config *acc_cfg =1359 &pjsua.config.acc_config[pjsua.config.acc_cnt];1360 1361 acc_cfg->id = id;1362 acc_cfg->contact = id;1363 }1364 }1365 1366 1367 /* Add another account as the last one.1368 * This account corresponds to local endpoint, and is user-less.1369 * This is also the default account.1370 */1371 if (pjsua.config.acc_cnt < PJ_ARRAY_SIZE(pjsua.config.acc_config)) {1372 pjsua.default_acc = pjsua.config.acc_cnt;1373 pjsua.acc[pjsua.default_acc].auto_gen = 1;1374 pjsua.config.acc_cnt++;1375 }1376 1377 1378 /* Initialize accounts: */1379 for (i=0; i<(int)pjsua.config.acc_cnt; ++i) {1380 status = init_acc(i);1381 if (status != PJ_SUCCESS) {1382 pjsua_perror(THIS_FILE, "Error initializing account", status);1383 goto on_error;1384 }1385 }1386 1387 1388 /* Create worker thread(s), if required: */1389 1390 for (i=0; i<(int)pjsua.config.thread_cnt; ++i) {1391 status = pj_thread_create( pjsua.pool, "pjsua", &pjsua_poll,1392 NULL, 0, 0, &pjsua.threads[i]);1393 if (status != PJ_SUCCESS) {1394 pjsua.quit_flag = 1;1395 for (--i; i>=0; --i) {1396 pj_thread_join(pjsua.threads[i]);1397 pj_thread_destroy(pjsua.threads[i]);1398 }1399 goto on_error;1400 }1401 }1402 1403 /* Start registration: */1404 1405 /* Create client registration session: */1406 for (i=0; i<(int)pjsua.config.acc_cnt; ++i) {1407 status = pjsua_regc_init(i);1408 if (status != PJ_SUCCESS)1409 goto on_error;1410 1411 /* Perform registration, if required. */1412 if (pjsua.acc[i].regc) {1413 pjsua_acc_set_registration(i, PJ_TRUE);1414 }1415 }1416 1417 1418 /* Re-init buddies */1419 count = pjsua.config.buddy_cnt;1420 pjsua.config.buddy_cnt = 0;1421 for (i=0; i<(int)count; ++i) {1422 pj_str_t uri = pjsua.config.buddy_uri[i];1423 pjsua_buddy_add(&uri, NULL);1424 }1425 1426 1427 /* Refresh presence */1428 pjsua_pres_refresh();1429 1430 1431 PJ_LOG(3,(THIS_FILE, "PJSUA version %s started", PJ_VERSION));1432 return PJ_SUCCESS;1433 1434 on_error:1435 pjsua_destroy();1436 return status;1437 }1438 1439 1440 458 /* Sleep with polling */ 1441 459 static void busy_sleep(unsigned msec) … … 1448 466 1449 467 do { 1450 pjsua_poll(NULL); 468 while (pjsua_handle_events(10) > 0) 469 ; 1451 470 pj_gettimeofday(&now); 1452 471 } while (PJ_TIME_VAL_LT(now, timeout)); 1453 472 } 1454 473 1455 /** 1456 * Get maxinum number of conference ports. 1457 */ 1458 PJ_DEF(unsigned) pjsua_conf_max_ports(void) 1459 { 1460 return pjsua.config.conf_ports; 1461 } 1462 1463 1464 /** 1465 * Enum all conference port ID. 1466 */ 1467 PJ_DEF(pj_status_t) pjsua_conf_enum_port_ids( pjsua_conf_port_id id[], 1468 unsigned *count) 1469 { 1470 return pjmedia_conf_enum_ports( pjsua.mconf, (unsigned*)id, count); 1471 } 1472 1473 1474 1475 /** 1476 * Get information about the specified conference port 1477 */ 1478 PJ_DEF(pj_status_t) pjsua_conf_get_port_info( pjsua_conf_port_id id, 1479 pjsua_conf_port_info *info) 1480 { 1481 pjmedia_conf_port_info cinfo; 1482 unsigned i, count; 1483 pj_status_t status; 1484 1485 status = pjmedia_conf_get_port_info( pjsua.mconf, id, &cinfo); 1486 if (status != PJ_SUCCESS) 1487 return status; 1488 1489 pj_memset(info, 0, sizeof(*info)); 1490 info->slot_id = id; 1491 info->name = cinfo.name; 1492 info->clock_rate = cinfo.clock_rate; 1493 info->channel_count = cinfo.channel_count; 1494 info->samples_per_frame = cinfo.samples_per_frame; 1495 info->bits_per_sample = cinfo.bits_per_sample; 1496 1497 /* Build array of listeners */ 1498 count = pjsua.config.conf_ports; 1499 for (i=0; i<count; ++i) { 1500 if (cinfo.listener[i]) { 1501 info->listeners[info->listener_cnt++] = i; 474 /* 475 * Destroy pjsua. 476 */ 477 PJ_DEF(pj_status_t) pjsua_destroy(void) 478 { 479 int i; /* Must be signed */ 480 481 /* Signal threads to quit: */ 482 pjsua_var.thread_quit_flag = 1; 483 484 /* Wait worker threads to quit: */ 485 for (i=0; i<(int)pjsua_var.ua_cfg.thread_cnt; ++i) { 486 if (pjsua_var.thread[i]) { 487 pj_thread_join(pjsua_var.thread[i]); 488 pj_thread_destroy(pjsua_var.thread[i]); 489 pjsua_var.thread[i] = NULL; 1502 490 } 1503 491 } 1504 1505 return PJ_SUCCESS;1506 }1507 1508 1509 /**1510 * Connect conference port.1511 */1512 PJ_DEF(pj_status_t) pjsua_conf_connect( pjsua_conf_port_id src_port,1513 pjsua_conf_port_id dst_port)1514 {1515 return pjmedia_conf_connect_port(pjsua.mconf, src_port, dst_port, 0);1516 }1517 1518 1519 /**1520 * Connect conference port connection.1521 */1522 PJ_DEF(pj_status_t) pjsua_conf_disconnect( pjsua_conf_port_id src_port,1523 pjsua_conf_port_id dst_port)1524 {1525 return pjmedia_conf_disconnect_port(pjsua.mconf, src_port, dst_port);1526 }1527 1528 1529 1530 /**1531 * Create a file player.1532 */1533 PJ_DEF(pj_status_t) pjsua_player_create( const pj_str_t *filename,1534 pjsua_player_id *id)1535 {1536 unsigned slot;1537 char path[128];1538 pjmedia_port *port;1539 pj_status_t status;1540 1541 if (pjsua.player_cnt >= PJ_ARRAY_SIZE(pjsua.player))1542 return PJ_ETOOMANY;1543 1544 pj_memcpy(path, filename->ptr, filename->slen);1545 path[filename->slen] = '\0';1546 status = pjmedia_wav_player_port_create(pjsua.pool, path,1547 pjsua.samples_per_frame *1548 1000 / pjsua.clock_rate,1549 0, 0, NULL,1550 &port);1551 if (status != PJ_SUCCESS)1552 return status;1553 1554 status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool,1555 port, filename, &slot);1556 if (status != PJ_SUCCESS) {1557 pjmedia_port_destroy(port);1558 return status;1559 }1560 1561 pjsua.player[pjsua.player_cnt].port = port;1562 pjsua.player[pjsua.player_cnt].slot = slot;1563 1564 if (id)1565 *id = pjsua.player_cnt;1566 1567 ++pjsua.player_cnt;1568 1569 return PJ_SUCCESS;1570 }1571 1572 1573 /**1574 * Get conference port associated with player.1575 */1576 PJ_DEF(pjsua_conf_port_id) pjsua_player_get_conf_port(pjsua_player_id id)1577 {1578 PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.player), PJ_EINVAL);1579 return pjsua.player[id].slot;1580 }1581 1582 1583 /**1584 * Re-wind playback.1585 */1586 PJ_DEF(pj_status_t) pjsua_player_set_pos(pjsua_player_id id,1587 pj_uint32_t samples)1588 {1589 PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.player), PJ_EINVAL);1590 PJ_ASSERT_RETURN(pjsua.player[id].port != NULL, PJ_EINVALIDOP);1591 1592 return pjmedia_wav_player_port_set_pos(pjsua.player[id].port, samples);1593 }1594 1595 1596 /**1597 * Get conference port associated with player.1598 */1599 PJ_DEF(pj_status_t) pjsua_player_destroy(pjsua_player_id id)1600 {1601 PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.player), PJ_EINVAL);1602 1603 if (pjsua.player[id].port) {1604 pjmedia_port_destroy(pjsua.player[id].port);1605 pjsua.player[id].port = NULL;1606 pjsua.player[id].slot = 0xFFFF;1607 pjsua.player_cnt--;1608 }1609 1610 return PJ_SUCCESS;1611 }1612 1613 1614 /**1615 * Create a file recorder.1616 */1617 PJ_DEF(pj_status_t) pjsua_recorder_create( const pj_str_t *filename,1618 pjsua_recorder_id *id)1619 {1620 unsigned slot;1621 char path[128];1622 pjmedia_port *port;1623 pj_status_t status;1624 1625 if (pjsua.recorder_cnt >= PJ_ARRAY_SIZE(pjsua.recorder))1626 return PJ_ETOOMANY;1627 1628 pj_memcpy(path, filename->ptr, filename->slen);1629 path[filename->slen] = '\0';1630 status = pjmedia_wav_writer_port_create(pjsua.pool, path,1631 pjsua.clock_rate, 1,1632 pjsua.samples_per_frame,1633 16, 0, 0, NULL,1634 &port);1635 if (status != PJ_SUCCESS)1636 return status;1637 1638 status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool,1639 port, filename, &slot);1640 if (status != PJ_SUCCESS) {1641 pjmedia_port_destroy(port);1642 return status;1643 }1644 1645 pjsua.recorder[pjsua.recorder_cnt].port = port;1646 pjsua.recorder[pjsua.recorder_cnt].slot = slot;1647 1648 if (*id)1649 *id = pjsua.recorder_cnt;1650 1651 ++pjsua.recorder_cnt;1652 1653 return PJ_SUCCESS;1654 }1655 1656 1657 /**1658 * Get conference port associated with recorder.1659 */1660 PJ_DEF(pjsua_conf_port_id) pjsua_recorder_get_conf_port(pjsua_recorder_id id)1661 {1662 PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.recorder), PJ_EINVAL);1663 return pjsua.recorder[id].slot;1664 }1665 1666 1667 /**1668 * Destroy recorder (will complete recording).1669 */1670 PJ_DEF(pj_status_t) pjsua_recorder_destroy(pjsua_recorder_id id)1671 {1672 PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.recorder), PJ_EINVAL);1673 1674 if (pjsua.recorder[id].port) {1675 pjmedia_port_destroy(pjsua.recorder[id].port);1676 pjsua.recorder[id].port = NULL;1677 pjsua.recorder[id].slot = 0xFFFF;1678 pjsua.recorder_cnt--;1679 }1680 1681 return PJ_SUCCESS;1682 }1683 1684 /**1685 * Enum sound devices.1686 */1687 PJ_DEF(pj_status_t) pjsua_enum_snd_devices( unsigned *count,1688 pjmedia_snd_dev_info info[])1689 {1690 int i, dev_count;1691 1692 dev_count = pjmedia_snd_get_dev_count();1693 if (dev_count > (int)*count)1694 dev_count = *count;1695 1696 for (i=0; i<dev_count; ++i) {1697 const pjmedia_snd_dev_info *dev_info;1698 dev_info = pjmedia_snd_get_dev_info(i);1699 pj_memcpy(&info[i], dev_info, sizeof(pjmedia_snd_dev_info));1700 }1701 1702 *count = dev_count;1703 return PJ_SUCCESS;1704 }1705 1706 1707 /**1708 * Select or change sound device.1709 */1710 PJ_DEF(pj_status_t) pjsua_set_snd_dev( int snd_capture_id,1711 int snd_player_id)1712 {1713 pjsua.config.snd_capture_id = snd_capture_id;1714 pjsua.config.snd_player_id = snd_player_id;1715 return PJ_SUCCESS;1716 }1717 1718 1719 /*1720 * Destroy pjsua.1721 */1722 PJ_DEF(pj_status_t) pjsua_destroy(void)1723 {1724 int i; /* Must be signed */1725 1726 /* Signal threads to quit: */1727 pjsua.quit_flag = 1;1728 1729 /* Wait worker threads to quit: */1730 for (i=0; i<(int)pjsua.config.thread_cnt; ++i) {1731 1732 if (pjsua.threads[i]) {1733 pj_thread_join(pjsua.threads[i]);1734 pj_thread_destroy(pjsua.threads[i]);1735 pjsua.threads[i] = NULL;1736 }1737 }1738 1739 492 1740 if (pjsua .endpt) {493 if (pjsua_var.endpt) { 1741 494 /* Terminate all calls. */ 1742 495 pjsua_call_hangup_all(); … … 1746 499 1747 500 /* Unregister, if required: */ 1748 for (i=0; i<(int)pjsua.config.acc_cnt; ++i) { 1749 if (pjsua.acc[i].regc) { 501 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { 502 if (!pjsua_var.acc[i].valid) 503 continue; 504 505 if (pjsua_var.acc[i].regc) { 1750 506 pjsua_acc_set_registration(i, PJ_FALSE); 1751 507 } … … 1757 513 } 1758 514 1759 /* If we have master port, destroying master port will recursively 1760 * destroy conference bridge, otherwise must destroy it manually. 515 /* Destroy media */ 516 pjsua_media_subsys_destroy(); 517 518 /* Destroy endpoint. */ 519 if (pjsua_var.endpt) { 520 pjsip_endpt_destroy(pjsua_var.endpt); 521 pjsua_var.endpt = NULL; 522 } 523 524 /* Destroy mutex */ 525 if (pjsua_var.mutex) { 526 pj_mutex_destroy(pjsua_var.mutex); 527 pjsua_var.mutex = NULL; 528 } 529 530 /* Destroy pool and pool factory. */ 531 if (pjsua_var.pool) { 532 pj_pool_release(pjsua_var.pool); 533 pjsua_var.pool = NULL; 534 pj_caching_pool_destroy(&pjsua_var.cp); 535 } 536 537 538 PJ_LOG(4,(THIS_FILE, "PJSUA destroyed...")); 539 540 /* End logging */ 541 if (pjsua_var.log_file) { 542 pj_file_close(pjsua_var.log_file); 543 pjsua_var.log_file = NULL; 544 } 545 546 /* Done. */ 547 return PJ_SUCCESS; 548 } 549 550 551 /** 552 * Application is recommended to call this function after all initialization 553 * is done, so that the library can do additional checking set up 554 * additional 555 * 556 * @return PJ_SUCCESS on success, or the appropriate error code. 557 */ 558 PJ_DEF(pj_status_t) pjsua_start(void) 559 { 560 pj_status_t status; 561 562 status = pjsua_call_subsys_start(); 563 if (status != PJ_SUCCESS) 564 return status; 565 566 status = pjsua_media_subsys_start(); 567 if (status != PJ_SUCCESS) 568 return status; 569 570 status = pjsua_pres_start(); 571 if (status != PJ_SUCCESS) 572 return status; 573 574 return PJ_SUCCESS; 575 } 576 577 578 /** 579 * Poll pjsua for events, and if necessary block the caller thread for 580 * the specified maximum interval (in miliseconds). 581 */ 582 PJ_DEF(int) pjsua_handle_events(unsigned msec_timeout) 583 { 584 unsigned count = 0; 585 pj_time_val tv; 586 pj_status_t status; 587 588 tv.sec = 0; 589 tv.msec = msec_timeout; 590 pj_time_val_normalize(&tv); 591 592 status = pjsip_endpt_handle_events2(pjsua_var.endpt, &tv, &count); 593 594 if (status != PJ_SUCCESS) 595 return -status; 596 597 return count; 598 } 599 600 601 /* 602 * Create memory pool. 603 */ 604 PJ_DEF(pj_pool_t*) pjsua_pool_create( const char *name, pj_size_t init_size, 605 pj_size_t increment) 606 { 607 /* Pool factory is thread safe, no need to lock */ 608 return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment, 609 NULL); 610 } 611 612 613 /* 614 * Internal function to get SIP endpoint instance of pjsua, which is 615 * needed for example to register module, create transports, etc. 616 * Probably is only valid after #pjsua_init() is called. 617 */ 618 PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void) 619 { 620 return pjsua_var.endpt; 621 } 622 623 /* 624 * Internal function to get media endpoint instance. 625 * Only valid after #pjsua_init() is called. 626 */ 627 PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void) 628 { 629 return pjsua_var.med_endpt; 630 } 631 632 633 /***************************************************************************** 634 * PJSUA SIP Transport API. 635 */ 636 637 /* 638 * Create and initialize SIP socket (and possibly resolve public 639 * address via STUN, depending on config). 640 */ 641 static pj_status_t create_sip_udp_sock(pj_in_addr bound_addr, 642 int port, 643 pj_bool_t use_stun, 644 const pjsua_stun_config *stun_param, 645 pj_sock_t *p_sock, 646 pj_sockaddr_in *p_pub_addr) 647 { 648 pjsua_stun_config stun; 649 pj_sock_t sock; 650 pj_status_t status; 651 652 PJSUA_LOCK(); 653 654 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock); 655 if (status != PJ_SUCCESS) { 656 pjsua_perror(THIS_FILE, "socket() error", status); 657 goto on_return; 658 } 659 660 status = pj_sock_bind_in(sock, bound_addr.s_addr, (pj_uint16_t)port); 661 if (status != PJ_SUCCESS) { 662 pjsua_perror(THIS_FILE, "bind() error", status); 663 pj_sock_close(sock); 664 goto on_return; 665 } 666 667 /* Copy and normalize STUN param */ 668 if (use_stun) { 669 pj_memcpy(&stun, stun_param, sizeof(*stun_param)); 670 pjsua_normalize_stun_config(&stun); 671 } else { 672 pj_memset(&stun, 0, sizeof(pjsua_stun_config)); 673 } 674 675 /* Get the published address, either by STUN or by resolving 676 * the name of local host. 1761 677 */ 1762 if (pjsua.master_port) { 1763 pjmedia_master_port_destroy(pjsua.master_port); 1764 pjsua.master_port = NULL; 678 if (stun.stun_srv1.slen) { 679 status = pj_stun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock, 680 &stun.stun_srv1, 681 stun.stun_port1, 682 &stun.stun_srv2, 683 stun.stun_port2, 684 p_pub_addr); 685 if (status != PJ_SUCCESS) { 686 pjsua_perror(THIS_FILE, "Error resolving with STUN", status); 687 pj_sock_close(sock); 688 goto on_return; 689 } 690 1765 691 } else { 1766 if (pjsua.snd_port) { 1767 pjmedia_snd_port_destroy(pjsua.snd_port); 1768 pjsua.snd_port = NULL; 692 693 const pj_str_t *hostname = pj_gethostname(); 694 struct pj_hostent he; 695 696 status = pj_gethostbyname(hostname, &he); 697 if (status != PJ_SUCCESS) { 698 pjsua_perror(THIS_FILE, "Unable to resolve local host", status); 699 pj_sock_close(sock); 700 goto on_return; 1769 701 } 1770 if (pjsua.mconf) { 1771 pjmedia_conf_destroy(pjsua.mconf); 1772 pjsua.mconf = NULL; 702 703 pj_memset(p_pub_addr, 0, sizeof(pj_sockaddr_in)); 704 p_pub_addr->sin_family = PJ_AF_INET; 705 p_pub_addr->sin_port = pj_htons((pj_uint16_t)port); 706 p_pub_addr->sin_addr = *(pj_in_addr*)he.h_addr; 707 } 708 709 *p_sock = sock; 710 711 on_return: 712 713 PJSUA_UNLOCK(); 714 715 PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d", 716 pj_inet_ntoa(p_pub_addr->sin_addr), 717 (int)pj_ntohs(p_pub_addr->sin_port))); 718 719 return status; 720 } 721 722 723 /* 724 * Create SIP transport. 725 */ 726 PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, 727 const pjsua_transport_config *cfg, 728 pjsua_transport_id *p_id) 729 { 730 pjsip_transport *tp; 731 unsigned id; 732 pj_status_t status; 733 734 PJSUA_LOCK(); 735 736 /* Find empty transport slot */ 737 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) { 738 if (pjsua_var.tpdata[id].tp == NULL) 739 break; 740 } 741 742 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) { 743 status = PJ_ETOOMANY; 744 pjsua_perror(THIS_FILE, "Error creating transport", status); 745 goto on_return; 746 } 747 748 /* Create the transport */ 749 if (type == PJSIP_TRANSPORT_UDP) { 750 751 pjsua_transport_config config; 752 pj_sock_t sock; 753 pj_sockaddr_in pub_addr; 754 pjsip_host_port addr_name; 755 756 /* Supply default config if it's not specified */ 757 if (cfg == NULL) { 758 pjsua_transport_config_default(&config); 759 cfg = &config; 1773 760 } 1774 } 1775 1776 /* Destroy file players */ 1777 for (i=0; i<PJ_ARRAY_SIZE(pjsua.player); ++i) { 1778 if (pjsua.player[i].port) { 1779 pjmedia_port_destroy(pjsua.player[i].port); 1780 pjsua.player[i].port = NULL; 761 762 /* Create the socket and possibly resolve the address with STUN */ 763 status = create_sip_udp_sock(cfg->ip_addr, cfg->port, cfg->use_stun, 764 &cfg->stun_config, &sock, &pub_addr); 765 if (status != PJ_SUCCESS) 766 goto on_return; 767 768 addr_name.host = pj_str(pj_inet_ntoa(pub_addr.sin_addr)); 769 addr_name.port = pj_ntohs(pub_addr.sin_port); 770 771 /* Create UDP transport */ 772 status = pjsip_udp_transport_attach( pjsua_var.endpt, sock, 773 &addr_name, 1, 774 &tp); 775 if (status != PJ_SUCCESS) { 776 pjsua_perror(THIS_FILE, "Error creating SIP UDP transport", 777 status); 778 pj_sock_close(sock); 779 goto on_return; 1781 780 } 1782 } 1783 1784 1785 /* Destroy file recorders */ 1786 for (i=0; i<PJ_ARRAY_SIZE(pjsua.recorder); ++i) { 1787 if (pjsua.recorder[i].port) { 1788 pjmedia_port_destroy(pjsua.recorder[i].port); 1789 pjsua.recorder[i].port = NULL; 1790 } 1791 } 1792 1793 1794 /* Close transports */ 1795 for (i=0; i<(int)pjsua.config.max_calls; ++i) { 1796 if (pjsua.calls[i].med_tp) { 1797 (*pjsua.calls[i].med_tp->op->destroy)(pjsua.calls[i].med_tp); 1798 pjsua.calls[i].med_tp = NULL; 1799 } 1800 } 1801 1802 /* Destroy media endpoint. */ 1803 if (pjsua.med_endpt) { 1804 1805 /* Shutdown all codecs: */ 1806 # if PJMEDIA_HAS_SPEEX_CODEC 1807 pjmedia_codec_speex_deinit(); 1808 # endif /* PJMEDIA_HAS_SPEEX_CODEC */ 1809 1810 # if PJMEDIA_HAS_GSM_CODEC 1811 pjmedia_codec_gsm_deinit(); 1812 # endif /* PJMEDIA_HAS_GSM_CODEC */ 1813 1814 # if PJMEDIA_HAS_G711_CODEC 1815 pjmedia_codec_g711_deinit(); 1816 # endif /* PJMEDIA_HAS_G711_CODEC */ 1817 1818 # if PJMEDIA_HAS_L16_CODEC 1819 pjmedia_codec_l16_deinit(); 1820 # endif /* PJMEDIA_HAS_L16_CODEC */ 1821 1822 1823 pjmedia_endpt_destroy(pjsua.med_endpt); 1824 pjsua.med_endpt = NULL; 1825 } 1826 1827 /* Destroy endpoint. */ 1828 if (pjsua.endpt) { 1829 pjsip_endpt_destroy(pjsua.endpt); 1830 pjsua.endpt = NULL; 1831 } 1832 1833 /* Destroy caching pool. */ 1834 pj_caching_pool_destroy(&pjsua.cp); 1835 1836 1837 PJ_LOG(4,(THIS_FILE, "PJSUA destroyed...")); 1838 1839 /* End logging */ 1840 logging_shutdown(); 1841 1842 /* Done. */ 781 782 } else { 783 status = PJSIP_EUNSUPTRANSPORT; 784 pjsua_perror(THIS_FILE, "Error creating transport", status); 785 goto on_return; 786 } 787 788 /* Save the transport */ 789 pjsua_var.tpdata[id].tp = tp; 790 791 /* Return the ID */ 792 if (p_id) *p_id = id; 793 794 status = PJ_SUCCESS; 795 796 on_return: 797 798 PJSUA_UNLOCK(); 1843 799 1844 800 return PJ_SUCCESS; … … 1846 802 1847 803 1848 /** 1849 * Get SIP endpoint instance. 1850 * Only valid after pjsua_init(). 1851 */ 1852 PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void) 1853 { 1854 return pjsua.endpt; 1855 } 1856 1857 /** 1858 * Get media endpoint instance. 1859 * Only valid after pjsua_init(). 1860 */ 1861 PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void) 1862 { 1863 return pjsua.med_endpt; 1864 } 1865 804 /* 805 * Register transport that has been created by application. 806 */ 807 PJ_DEF(pj_status_t) pjsua_transport_register( pjsip_transport *tp, 808 pjsua_transport_id *p_id) 809 { 810 unsigned id; 811 812 PJSUA_LOCK(); 813 814 /* Find empty transport slot */ 815 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) { 816 if (pjsua_var.tpdata[id].tp == NULL) 817 break; 818 } 819 820 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) { 821 pjsua_perror(THIS_FILE, "Error creating transport", PJ_ETOOMANY); 822 PJSUA_UNLOCK(); 823 return PJ_ETOOMANY; 824 } 825 826 /* Save the transport */ 827 pjsua_var.tpdata[id].tp = tp; 828 829 /* Return the ID */ 830 if (p_id) *p_id = id; 831 832 PJSUA_UNLOCK(); 833 834 return PJ_SUCCESS; 835 } 836 837 838 /* 839 * Enumerate all transports currently created in the system. 840 */ 841 PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[], 842 unsigned *p_count ) 843 { 844 unsigned i, count; 845 846 PJSUA_LOCK(); 847 848 for (i=0, count=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata) && count<*p_count; 849 ++i) 850 { 851 if (!pjsua_var.tpdata[i].tp) 852 continue; 853 854 id[count++] = i; 855 } 856 857 *p_count = count; 858 859 PJSUA_UNLOCK(); 860 861 return PJ_SUCCESS; 862 } 863 864 865 /* 866 * Get information about transports. 867 */ 868 PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id, 869 pjsua_transport_info *info) 870 { 871 pjsip_transport *tp; 872 873 pj_memset(info, 0, sizeof(*info)); 874 875 /* Make sure id is in range. */ 876 PJ_ASSERT_RETURN(id>=0 && id<PJ_ARRAY_SIZE(pjsua_var.tpdata), PJ_EINVAL); 877 878 /* Make sure that transport exists */ 879 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].tp != NULL, PJ_EINVAL); 880 881 PJSUA_LOCK(); 882 883 tp = pjsua_var.tpdata[id].tp; 884 if (tp == NULL) { 885 PJSUA_UNLOCK(); 886 return PJ_EINVALIDOP; 887 } 888 889 info->id = id; 890 info->type = tp->key.type; 891 info->type_name = pj_str(tp->type_name); 892 info->info = pj_str(tp->info); 893 info->flag = tp->flag; 894 info->addr_len = tp->addr_len; 895 info->local_addr = tp->local_addr; 896 info->local_name = tp->local_name; 897 info->usage_count = pj_atomic_get(tp->ref_cnt); 898 899 PJSUA_UNLOCK(); 900 901 return PJ_EINVALIDOP; 902 } 903 904 905 /* 906 * Disable a transport or re-enable it. 907 */ 908 PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id, 909 pj_bool_t enabled) 910 { 911 /* Make sure id is in range. */ 912 PJ_ASSERT_RETURN(id>=0 && id<PJ_ARRAY_SIZE(pjsua_var.tpdata), PJ_EINVAL); 913 914 /* Make sure that transport exists */ 915 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].tp != NULL, PJ_EINVAL); 916 917 918 /* To be done!! */ 919 PJ_TODO(pjsua_transport_set_enable); 920 921 return PJ_EINVALIDOP; 922 } 923 924 925 /* 926 * Close the transport. 927 */ 928 PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id, 929 pj_bool_t force ) 930 { 931 /* Make sure id is in range. */ 932 PJ_ASSERT_RETURN(id>=0 && id<PJ_ARRAY_SIZE(pjsua_var.tpdata), PJ_EINVAL); 933 934 /* Make sure that transport exists */ 935 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].tp != NULL, PJ_EINVAL); 936 937 938 /* To be done!! */ 939 940 941 PJ_TODO(pjsua_transport_close); 942 943 return PJ_EINVALIDOP; 944 } 945 946 947 /* 948 * Add additional headers etc in msg_data specified by application 949 * when sending requests. 950 */ 951 void pjsua_process_msg_data(pjsip_tx_data *tdata, 952 const pjsua_msg_data *msg_data) 953 { 954 pj_bool_t allow_body; 955 const pjsip_hdr *hdr; 956 957 if (!msg_data) 958 return; 959 960 hdr = msg_data->hdr_list.next; 961 while (hdr && hdr != &msg_data->hdr_list) { 962 pjsip_hdr *new_hdr; 963 964 new_hdr = pjsip_hdr_clone(tdata->pool, hdr); 965 pjsip_msg_add_hdr(tdata->msg, new_hdr); 966 967 hdr = hdr->next; 968 } 969 970 allow_body = (tdata->msg->body == NULL); 971 972 if (allow_body && msg_data->content_type.slen && msg_data->msg_body.slen) { 973 pjsip_media_type ctype; 974 pjsip_msg_body *body; 975 976 pjsua_parse_media_type(tdata->pool, &msg_data->content_type, &ctype); 977 body = pjsip_msg_body_create(tdata->pool, &ctype.type, &ctype.subtype, 978 &msg_data->msg_body); 979 tdata->msg->body = body; 980 } 981 } 982 983 984 /* 985 * Add route_set to outgoing requests 986 */ 987 void pjsua_set_msg_route_set( pjsip_tx_data *tdata, 988 const pjsip_route_hdr *route_set ) 989 { 990 const pjsip_route_hdr *r; 991 992 r = route_set->next; 993 while (r != route_set) { 994 pjsip_route_hdr *new_r; 995 996 new_r = pjsip_hdr_clone(tdata->pool, r); 997 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)new_r); 998 999 r = r->next; 1000 } 1001 } 1002 1003 1004 /* 1005 * Simple version of MIME type parsing (it doesn't support parameters) 1006 */ 1007 void pjsua_parse_media_type( pj_pool_t *pool, 1008 const pj_str_t *mime, 1009 pjsip_media_type *media_type) 1010 { 1011 pj_str_t tmp; 1012 char *pos; 1013 1014 pj_memset(media_type, 0, sizeof(*media_type)); 1015 1016 pj_strdup_with_null(pool, &tmp, mime); 1017 1018 pos = pj_strchr(&tmp, '/'); 1019 if (pos) { 1020 media_type->type.ptr = tmp.ptr; 1021 media_type->type.slen = (pos-tmp.ptr); 1022 media_type->subtype.ptr = pos+1; 1023 media_type->subtype.slen = tmp.ptr+tmp.slen-pos-1; 1024 } else { 1025 media_type->type = tmp; 1026 } 1027 } 1028 1029 1030 /* 1031 * Verify that valid SIP url is given. 1032 */ 1033 PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url) 1034 { 1035 pjsip_uri *p; 1036 pj_pool_t *pool; 1037 char *url; 1038 int len = (c_url ? pj_ansi_strlen(c_url) : 0); 1039 1040 if (!len) return -1; 1041 1042 pool = pj_pool_create(&pjsua_var.cp.factory, "check%p", 1024, 0, NULL); 1043 if (!pool) return -1; 1044 1045 url = pj_pool_alloc(pool, len+1); 1046 pj_ansi_strcpy(url, c_url); 1047 1048 p = pjsip_parse_uri(pool, url, len, 0); 1049 if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0) 1050 p = NULL; 1051 1052 pj_pool_release(pool); 1053 return p ? 0 : -1; 1054 } -
pjproject/trunk/pjsip/src/pjsua-lib/pjsua_im.c
r492 r503 18 18 */ 19 19 #include <pjsua-lib/pjsua.h> 20 #include <pj/log.h> 21 #include "pjsua_imp.h" 22 23 /* 24 * pjsua_im.c 25 * 26 * To handle incoming MESSAGE outside dialog. 27 * Incoming MESSAGE inside dialog is hanlded in pjsua_call.c. 28 */ 29 30 #define THIS_FILE "pjsua_im.c" 20 #include <pjsua-lib/pjsua_internal.h> 21 22 23 #define THIS_FILE "pjsua_im.h" 31 24 32 25 … … 50 43 /* Proto */ 51 44 static pj_bool_t im_on_rx_request(pjsip_rx_data *rdata); 45 52 46 53 47 /* The module instance. */ … … 137 131 * This may trigger pjsua_ui_on_pager() or pjsua_ui_on_typing(). 138 132 */ 139 void pjsua_im_process_pager(int call_i ndex, const pj_str_t *from,133 void pjsua_im_process_pager(int call_id, const pj_str_t *from, 140 134 const pj_str_t *to, pjsip_rx_data *rdata) 141 135 { 136 pjsip_contact_hdr *contact_hdr; 137 pj_str_t contact; 142 138 pjsip_msg_body *body = rdata->msg_info.msg->body; 143 139 144 140 /* Body MUST have been checked before */ 145 141 pj_assert(body != NULL); 142 143 144 /* Build remote contact */ 145 contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, 146 NULL); 147 if (contact_hdr) { 148 contact.ptr = pj_pool_alloc(rdata->tp_info.pool, 149 PJSIP_MAX_URL_SIZE); 150 contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, 151 contact_hdr->uri, contact.ptr, 152 PJSIP_MAX_URL_SIZE); 153 } else { 154 contact.slen = 0; 155 } 156 146 157 147 158 if (pj_stricmp(&body->content_type.type, &STR_MIME_TEXT)==0 && 148 159 pj_stricmp(&body->content_type.subtype, &STR_MIME_PLAIN)==0) 149 160 { 150 pj_str_t text; 151 152 /* Build the text. */ 153 text.ptr = rdata->msg_info.msg->body->data; 154 text.slen = rdata->msg_info.msg->body->len; 155 156 if (pjsua.cb.on_pager) 157 (*pjsua.cb.on_pager)(call_index, from, to, &text); 161 const pj_str_t mime_text_plain = pj_str("text/plain"); 162 pj_str_t text_body; 163 164 /* Save text body */ 165 text_body.ptr = rdata->msg_info.msg->body->data; 166 text_body.slen = rdata->msg_info.msg->body->len; 167 168 if (pjsua_var.ua_cfg.cb.on_pager) { 169 (*pjsua_var.ua_cfg.cb.on_pager)(call_id, from, to, &contact, 170 &mime_text_plain, &text_body); 171 } 158 172 159 173 } else { 160 161 174 /* Expecting typing indication */ 162 163 175 pj_status_t status; 164 176 pj_bool_t is_typing; … … 172 184 } 173 185 174 if (pjsua.cb.on_typing) 175 (*pjsua.cb.on_typing)(call_index, from, to, is_typing); 186 if (pjsua_var.ua_cfg.cb.on_typing) { 187 (*pjsua_var.ua_cfg.cb.on_typing)(call_id, from, to, &contact, 188 is_typing); 189 } 176 190 } 177 191 … … 211 225 pj_list_push_back(&hdr_list, accept_hdr); 212 226 213 pjsip_endpt_respond_stateless(pjsua .endpt, rdata,227 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 214 228 PJSIP_SC_NOT_ACCEPTABLE_HERE, NULL, 215 229 &hdr_list, NULL); … … 220 234 * the UI takes too long to process the message. 221 235 */ 222 status = pjsip_endpt_respond( pjsua .endpt, NULL, rdata, 200, NULL,236 status = pjsip_endpt_respond( pjsua_var.endpt, NULL, rdata, 200, NULL, 223 237 NULL, NULL, NULL); 224 238 … … 262 276 static void im_callback(void *token, pjsip_event *e) 263 277 { 264 pj _str_t *text= token;278 pjsua_im_data *im_data = token; 265 279 266 280 if (e->type == PJSIP_EVENT_TSX_STATE) { 267 281 268 282 pjsip_transaction *tsx = e->body.tsx_state.tsx; 283 284 /* Ignore provisional response, if any */ 285 if (tsx->status_code < 200) 286 return; 287 288 289 /* Handle authentication challenges */ 290 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG && 291 (tsx->status_code == 401 || tsx->status_code == 407)) 292 { 293 pjsip_rx_data *rdata = e->body.tsx_state.src.rdata; 294 pjsip_tx_data *tdata; 295 pjsip_auth_clt_sess auth; 296 pj_status_t status; 297 298 PJ_LOG(4,(THIS_FILE, "Resending IM with authentication")); 299 300 /* Create temporary authentication session */ 301 pjsip_auth_clt_init(&auth,pjsua_var.endpt,rdata->tp_info.pool, 0); 302 303 pjsip_auth_clt_set_credentials(&auth, 304 pjsua_var.acc[im_data->acc_id].cred_cnt, 305 pjsua_var.acc[im_data->acc_id].cred); 306 307 status = pjsip_auth_clt_reinit_req(&auth, rdata, tsx->last_tx, 308 &tdata); 309 if (status == PJ_SUCCESS) { 310 pjsua_im_data *im_data2; 311 312 /* Must duplicate im_data */ 313 im_data2 = pjsua_im_data_dup(tdata->pool, im_data); 314 315 /* Re-send request */ 316 status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1, 317 im_data2, &im_callback); 318 if (status == PJ_SUCCESS) { 319 /* Done */ 320 return; 321 } 322 } 323 } 269 324 270 325 if (tsx->status_code/100 == 2) { 271 326 PJ_LOG(4,(THIS_FILE, 272 327 "Message \'%s\' delivered successfully", 273 text->ptr));328 im_data->body.ptr)); 274 329 } else { 275 330 PJ_LOG(3,(THIS_FILE, 276 "Failed to deliver message \'%s\': %s [st_code=%d]", 277 text->ptr, 278 pjsip_get_status_text(tsx->status_code)->ptr, 279 tsx->status_code)); 331 "Failed to deliver message \'%s\': %d/%.*s", 332 im_data->body.ptr, 333 tsx->status_code, 334 (int)tsx->status_text.slen, 335 tsx->status_text.ptr)); 280 336 } 281 } 282 } 283 284 285 /** 286 * Send IM outside dialog. 287 */ 288 PJ_DEF(pj_status_t) pjsua_im_send(int acc_index, const pj_str_t *dst_uri, 289 const pj_str_t *str) 337 338 if (pjsua_var.ua_cfg.cb.on_pager_status) 339 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id, 340 &im_data->to, 341 &im_data->body, 342 im_data->user_data, 343 tsx->status_code, 344 &tsx->status_text); 345 } 346 } 347 348 349 /* Outgoing typing indication callback. 350 * (used to reauthenticate request) 351 */ 352 static void typing_callback(void *token, pjsip_event *e) 353 { 354 pjsua_im_data *im_data = token; 355 356 if (e->type == PJSIP_EVENT_TSX_STATE) { 357 358 pjsip_transaction *tsx = e->body.tsx_state.tsx; 359 360 /* Ignore provisional response, if any */ 361 if (tsx->status_code < 200) 362 return; 363 364 /* Handle authentication challenges */ 365 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG && 366 (tsx->status_code == 401 || tsx->status_code == 407)) 367 { 368 pjsip_rx_data *rdata = e->body.tsx_state.src.rdata; 369 pjsip_tx_data *tdata; 370 pjsip_auth_clt_sess auth; 371 pj_status_t status; 372 373 PJ_LOG(4,(THIS_FILE, "Resending IM with authentication")); 374 375 /* Create temporary authentication session */ 376 pjsip_auth_clt_init(&auth,pjsua_var.endpt,rdata->tp_info.pool, 0); 377 378 pjsip_auth_clt_set_credentials(&auth, 379 pjsua_var.acc[im_data->acc_id].cred_cnt, 380 pjsua_var.acc[im_data->acc_id].cred); 381 382 status = pjsip_auth_clt_reinit_req(&auth, rdata, tsx->last_tx, 383 &tdata); 384 if (status == PJ_SUCCESS) { 385 pjsua_im_data *im_data2; 386 387 /* Must duplicate im_data */ 388 im_data2 = pjsua_im_data_dup(tdata->pool, im_data); 389 390 /* Re-send request */ 391 status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1, 392 im_data2, &typing_callback); 393 if (status == PJ_SUCCESS) { 394 /* Done */ 395 return; 396 } 397 } 398 } 399 400 } 401 } 402 403 404 /* 405 * Send instant messaging outside dialog, using the specified account for 406 * route set and authentication. 407 */ 408 PJ_DEF(pj_status_t) pjsua_im_send( pjsua_acc_id acc_id, 409 const pj_str_t *to, 410 const pj_str_t *mime_type, 411 const pj_str_t *content, 412 const pjsua_msg_data *msg_data, 413 void *user_data) 290 414 { 291 415 pjsip_tx_data *tdata; 416 const pj_str_t mime_text_plain = pj_str("text/plain"); 292 417 const pj_str_t STR_CONTACT = { "Contact", 7 }; 293 const pj_str_t mime_text = pj_str("text"); 294 const pj_str_t mime_plain = pj_str("plain"); 295 pj_str_t *text; 418 pjsip_media_type media_type; 419 pjsua_im_data *im_data; 296 420 pj_status_t status; 297 421 422 /* To and message body must be specified. */ 423 PJ_ASSERT_RETURN(to && content, PJ_EINVAL); 424 298 425 /* Create request. */ 299 status = pjsip_endpt_create_request(pjsua .endpt, &pjsip_message_method,300 dst_uri,301 &pjsua .config.acc_config[acc_index].id,302 dst_uri, NULL, NULL, -1, NULL, &tdata);426 status = pjsip_endpt_create_request(pjsua_var.endpt, 427 &pjsip_message_method, to, 428 &pjsua_var.acc[acc_id].cfg.id, 429 to, NULL, NULL, -1, NULL, &tdata); 303 430 if (status != PJ_SUCCESS) { 304 431 pjsua_perror(THIS_FILE, "Unable to create request", status); … … 314 441 pjsip_generic_string_hdr_create(tdata->pool, 315 442 &STR_CONTACT, 316 &pjsua.config.acc_config[acc_index].contact)); 317 318 /* Duplicate text. 319 * We need to keep the text because we will display it when we fail to 320 * send the message. 443 &pjsua_var.acc[acc_id].cfg.contact)); 444 445 /* Create IM data to keep message details and give it back to 446 * application on the callback 321 447 */ 322 text = pj_pool_alloc(tdata->pool, sizeof(pj_str_t)); 323 pj_strdup_with_null(tdata->pool, text, str); 448 im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data)); 449 im_data->acc_id = acc_id; 450 im_data->call_id = PJSUA_INVALID_ID; 451 pj_strdup_with_null(tdata->pool, &im_data->to, to); 452 pj_strdup_with_null(tdata->pool, &im_data->body, content); 453 im_data->user_data = user_data; 454 455 456 /* Set default media type if none is specified */ 457 if (mime_type == NULL) { 458 mime_type = &mime_text_plain; 459 } 460 461 /* Parse MIME type */ 462 pjsua_parse_media_type(tdata->pool, mime_type, &media_type); 324 463 325 464 /* Add message body */ 326 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &mime_text, 327 &mime_plain, text); 465 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &media_type.type, 466 &media_type.subtype, 467 &im_data->body); 328 468 if (tdata->msg->body == NULL) { 329 469 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM); … … 332 472 } 333 473 474 /* Add additional headers etc. */ 475 pjsua_process_msg_data(tdata, msg_data); 476 477 /* Add route set */ 478 pjsua_set_msg_route_set(tdata, &pjsua_var.acc[acc_id].route_set); 479 334 480 /* Send request (statefully) */ 335 status = pjsip_endpt_send_request( pjsua .endpt, tdata, -1,336 text, &im_callback);481 status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1, 482 im_data, &im_callback); 337 483 if (status != PJ_SUCCESS) { 338 484 pjsua_perror(THIS_FILE, "Unable to send request", status); … … 344 490 345 491 346 /* *492 /* 347 493 * Send typing indication outside dialog. 348 494 */ 349 PJ_DEF(pj_status_t) pjsua_im_typing(int acc_index, const pj_str_t *dst_uri, 350 pj_bool_t is_typing) 495 PJ_DEF(pj_status_t) pjsua_im_typing( pjsua_acc_id acc_id, 496 const pj_str_t *to, 497 pj_bool_t is_typing, 498 const pjsua_msg_data *msg_data) 351 499 { 352 500 const pj_str_t STR_CONTACT = { "Contact", 7 }; 501 pjsua_im_data *im_data; 353 502 pjsip_tx_data *tdata; 354 503 pj_status_t status; 355 504 356 505 /* Create request. */ 357 status = pjsip_endpt_create_request( pjsua.endpt, &pjsip_message_method, 358 dst_uri, 359 &pjsua.config.acc_config[acc_index].id, 360 dst_uri, NULL, NULL, -1, NULL, &tdata); 506 status = pjsip_endpt_create_request( pjsua_var.endpt, &pjsip_message_method, 507 to, &pjsua_var.acc[acc_id].cfg.id, 508 to, NULL, NULL, -1, NULL, &tdata); 361 509 if (status != PJ_SUCCESS) { 362 510 pjsua_perror(THIS_FILE, "Unable to create request", status); … … 374 522 pjsip_generic_string_hdr_create(tdata->pool, 375 523 &STR_CONTACT, 376 &pjsua .config.acc_config[acc_index].contact));524 &pjsua_var.acc[acc_id].cfg.contact)); 377 525 378 526 … … 381 529 NULL, NULL, -1); 382 530 531 /* Add additional headers etc. */ 532 pjsua_process_msg_data(tdata, msg_data); 533 534 /* Create data to reauthenticate */ 535 im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data)); 536 im_data->acc_id = acc_id; 537 383 538 /* Send request (statefully) */ 384 status = pjsip_endpt_send_request( pjsua .endpt, tdata, -1,385 NULL, NULL);539 status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1, 540 im_data, &typing_callback); 386 541 if (status != PJ_SUCCESS) { 387 542 pjsua_perror(THIS_FILE, "Unable to send request", status); … … 402 557 403 558 /* Register module */ 404 status = pjsip_endpt_register_module(pjsua .endpt, &mod_pjsua_im);559 status = pjsip_endpt_register_module(pjsua_var.endpt, &mod_pjsua_im); 405 560 if (status != PJ_SUCCESS) 406 561 return status; 407 562 408 563 /* Register support for MESSAGE method. */ 409 pjsip_endpt_add_capability( pjsua .endpt, &mod_pjsua_im, PJSIP_H_ALLOW,564 pjsip_endpt_add_capability( pjsua_var.endpt, &mod_pjsua_im, PJSIP_H_ALLOW, 410 565 NULL, 1, &msg_tag); 411 566 -
pjproject/trunk/pjsip/src/pjsua-lib/pjsua_pres.c
r492 r503 18 18 */ 19 19 #include <pjsua-lib/pjsua.h> 20 #include "pjsua_imp.h" 21 22 /* 23 * pjsua_pres.c 24 * 25 * Presence related stuffs. 26 */ 20 #include <pjsua-lib/pjsua_internal.h> 21 27 22 28 23 #define THIS_FILE "pjsua_pres.c" 29 24 30 25 31 32 /* ************************************************************************** 33 * THE FOLLOWING PART HANDLES SERVER SUBSCRIPTION 34 * ************************************************************************** 26 /* 27 * Get total number of buddies. 28 */ 29 PJ_DEF(unsigned) pjsua_get_buddy_count(void) 30 { 31 return pjsua_var.buddy_cnt; 32 } 33 34 35 /* 36 * Check if buddy ID is valid. 37 */ 38 PJ_DEF(pj_bool_t) pjsua_buddy_is_valid(pjsua_buddy_id buddy_id) 39 { 40 return buddy_id>=0 && buddy_id<PJ_ARRAY_SIZE(pjsua_var.buddy) && 41 pjsua_var.buddy[buddy_id].uri.slen != 0; 42 } 43 44 45 /* 46 * Enum buddy IDs. 47 */ 48 PJ_DEF(pj_status_t) pjsua_enum_buddies( pjsua_buddy_id ids[], 49 unsigned *count) 50 { 51 unsigned i, c; 52 53 PJ_ASSERT_RETURN(ids && count, PJ_EINVAL); 54 55 PJSUA_LOCK(); 56 57 for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) { 58 if (!pjsua_var.buddy[i].uri.slen) 59 continue; 60 ids[c] = i; 61 ++c; 62 } 63 64 *count = c; 65 66 PJSUA_UNLOCK(); 67 68 return PJ_SUCCESS; 69 70 } 71 72 73 /* 74 * Get detailed buddy info. 75 */ 76 PJ_DEF(pj_status_t) pjsua_buddy_get_info( pjsua_buddy_id buddy_id, 77 pjsua_buddy_info *info) 78 { 79 int total=0; 80 pjsua_buddy *buddy; 81 82 PJ_ASSERT_RETURN(buddy_id>=0 && 83 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy), 84 PJ_EINVAL); 85 86 PJSUA_LOCK(); 87 88 pj_memset(info, 0, sizeof(pjsua_buddy_info)); 89 90 buddy = &pjsua_var.buddy[buddy_id]; 91 info->id = buddy->index; 92 if (pjsua_var.buddy[buddy_id].uri.slen == 0) { 93 PJSUA_UNLOCK(); 94 return PJ_SUCCESS; 95 } 96 97 /* uri */ 98 info->uri.ptr = info->buf_ + total; 99 pj_strncpy(&info->uri, &buddy->uri, sizeof(info->buf_)-total); 100 total += info->uri.slen; 101 102 /* contact */ 103 info->contact.ptr = info->buf_ + total; 104 pj_strncpy(&info->contact, &buddy->contact, sizeof(info->buf_)-total); 105 total += info->contact.slen; 106 107 /* status and status text */ 108 if (buddy->sub == NULL || buddy->status.info_cnt==0) { 109 info->status = PJSUA_BUDDY_STATUS_UNKNOWN; 110 info->status_text = pj_str("?"); 111 } else if (pjsua_var.buddy[buddy_id].status.info[0].basic_open) { 112 info->status = PJSUA_BUDDY_STATUS_ONLINE; 113 info->status_text = pj_str("Online"); 114 } else { 115 info->status = PJSUA_BUDDY_STATUS_OFFLINE; 116 info->status_text = pj_str("Offline"); 117 } 118 119 /* monitor pres */ 120 info->monitor_pres = buddy->monitor; 121 122 PJSUA_UNLOCK(); 123 return PJ_SUCCESS; 124 } 125 126 127 /* 128 * Reset buddy descriptor. 129 */ 130 static void reset_buddy(pjsua_buddy_id id) 131 { 132 pj_memset(&pjsua_var.buddy[id], 0, sizeof(pjsua_var.buddy[id])); 133 pjsua_var.buddy[id].index = id; 134 } 135 136 137 /* 138 * Add new buddy. 139 */ 140 PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg, 141 pjsua_buddy_id *p_buddy_id) 142 { 143 pjsip_name_addr *url; 144 pjsip_sip_uri *sip_uri; 145 int index; 146 pj_str_t tmp; 147 148 PJ_ASSERT_RETURN(pjsua_var.buddy_cnt <= 149 PJ_ARRAY_SIZE(pjsua_var.buddy), 150 PJ_ETOOMANY); 151 152 PJSUA_LOCK(); 153 154 /* Find empty slot */ 155 for (index=0; index<PJ_ARRAY_SIZE(pjsua_var.buddy); ++index) { 156 if (pjsua_var.buddy[index].uri.slen == 0) 157 break; 158 } 159 160 /* Expect to find an empty slot */ 161 if (index == PJ_ARRAY_SIZE(pjsua_var.buddy)) { 162 PJSUA_UNLOCK(); 163 /* This shouldn't happen */ 164 pj_assert(!"index < PJ_ARRAY_SIZE(pjsua_var.buddy)"); 165 return PJ_ETOOMANY; 166 } 167 168 169 /* Get name and display name for buddy */ 170 pj_strdup_with_null(pjsua_var.pool, &tmp, &cfg->uri); 171 url = (pjsip_name_addr*)pjsip_parse_uri(pjsua_var.pool, tmp.ptr, tmp.slen, 172 PJSIP_PARSE_URI_AS_NAMEADDR); 173 174 if (url == NULL) { 175 pjsua_perror(THIS_FILE, "Unable to add buddy", PJSIP_EINVALIDURI); 176 PJSUA_UNLOCK(); 177 return PJSIP_EINVALIDURI; 178 } 179 180 /* Reset buddy, to make sure everything is cleared with default 181 * values 182 */ 183 reset_buddy(index); 184 185 /* Save URI */ 186 pjsua_var.buddy[index].uri = tmp; 187 188 sip_uri = (pjsip_sip_uri*) url->uri; 189 pjsua_var.buddy[index].name = sip_uri->user; 190 pjsua_var.buddy[index].display = url->display; 191 pjsua_var.buddy[index].host = sip_uri->host; 192 pjsua_var.buddy[index].port = sip_uri->port; 193 pjsua_var.buddy[index].monitor = cfg->subscribe; 194 if (pjsua_var.buddy[index].port == 0) 195 pjsua_var.buddy[index].port = 5060; 196 197 if (p_buddy_id) 198 *p_buddy_id = index; 199 200 pjsua_var.buddy_cnt++; 201 202 pjsua_buddy_subscribe_pres(index, cfg->subscribe); 203 204 PJSUA_UNLOCK(); 205 206 return PJ_SUCCESS; 207 } 208 209 210 /* 211 * Delete buddy. 212 */ 213 PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id) 214 { 215 PJ_ASSERT_RETURN(buddy_id>=0 && 216 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy), 217 PJ_EINVAL); 218 219 PJSUA_LOCK(); 220 221 if (pjsua_var.buddy[buddy_id].uri.slen == 0) { 222 PJSUA_UNLOCK(); 223 return PJ_SUCCESS; 224 } 225 226 /* Unsubscribe presence */ 227 pjsua_buddy_subscribe_pres(buddy_id, PJ_FALSE); 228 229 /* Remove buddy */ 230 pjsua_var.buddy[buddy_id].uri.slen = 0; 231 pjsua_var.buddy_cnt--; 232 233 /* Reset buddy struct */ 234 reset_buddy(buddy_id); 235 236 PJSUA_UNLOCK(); 237 return PJ_SUCCESS; 238 } 239 240 241 /* 242 * Enable/disable buddy's presence monitoring. 243 */ 244 PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id buddy_id, 245 pj_bool_t subscribe) 246 { 247 pjsua_buddy *buddy; 248 249 PJ_ASSERT_RETURN(buddy_id>=0 && 250 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy), 251 PJ_EINVAL); 252 253 PJSUA_LOCK(); 254 255 buddy = &pjsua_var.buddy[buddy_id]; 256 buddy->monitor = subscribe; 257 pjsua_pres_refresh(); 258 259 PJSUA_UNLOCK(); 260 261 return PJ_SUCCESS; 262 } 263 264 265 /* 266 * Dump presence subscriptions to log file. 267 */ 268 PJ_DEF(void) pjsua_pres_dump(pj_bool_t verbose) 269 { 270 unsigned acc_id; 271 unsigned i; 272 273 274 PJSUA_LOCK(); 275 276 /* 277 * When no detail is required, just dump number of server and client 278 * subscriptions. 279 */ 280 if (verbose == PJ_FALSE) { 281 282 int count = 0; 283 284 for (acc_id=0; acc_id<PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) { 285 286 if (!pjsua_var.acc[acc_id].valid) 287 continue; 288 289 if (!pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) { 290 struct pjsua_srv_pres *uapres; 291 292 uapres = pjsua_var.acc[acc_id].pres_srv_list.next; 293 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) { 294 ++count; 295 uapres = uapres->next; 296 } 297 } 298 } 299 300 PJ_LOG(3,(THIS_FILE, "Number of server/UAS subscriptions: %d", 301 count)); 302 303 count = 0; 304 305 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) { 306 if (pjsua_var.buddy[i].uri.slen == 0) 307 continue; 308 if (pjsua_var.buddy[i].sub) { 309 ++count; 310 } 311 } 312 313 PJ_LOG(3,(THIS_FILE, "Number of client/UAC subscriptions: %d", 314 count)); 315 PJSUA_UNLOCK(); 316 return; 317 } 318 319 320 /* 321 * Dumping all server (UAS) subscriptions 322 */ 323 PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:")); 324 325 for (acc_id=0; acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) { 326 327 if (!pjsua_var.acc[acc_id].valid) 328 continue; 329 330 PJ_LOG(3,(THIS_FILE, " %.*s", 331 (int)pjsua_var.acc[acc_id].cfg.id.slen, 332 pjsua_var.acc[acc_id].cfg.id.ptr)); 333 334 if (pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) { 335 336 PJ_LOG(3,(THIS_FILE, " - none - ")); 337 338 } else { 339 struct pjsua_srv_pres *uapres; 340 341 uapres = pjsua_var.acc[acc_id].pres_srv_list.next; 342 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) { 343 344 PJ_LOG(3,(THIS_FILE, " %10s %s", 345 pjsip_evsub_get_state_name(uapres->sub), 346 uapres->remote)); 347 348 uapres = uapres->next; 349 } 350 } 351 } 352 353 /* 354 * Dumping all client (UAC) subscriptions 355 */ 356 PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:")); 357 358 if (pjsua_var.buddy_cnt == 0) { 359 360 PJ_LOG(3,(THIS_FILE, " - no buddy list - ")); 361 362 } else { 363 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) { 364 365 if (pjsua_var.buddy[i].uri.slen == 0) 366 continue; 367 368 if (pjsua_var.buddy[i].sub) { 369 PJ_LOG(3,(THIS_FILE, " %10s %.*s", 370 pjsip_evsub_get_state_name(pjsua_var.buddy[i].sub), 371 (int)pjsua_var.buddy[i].uri.slen, 372 pjsua_var.buddy[i].uri.ptr)); 373 } else { 374 PJ_LOG(3,(THIS_FILE, " %10s %.*s", 375 "(null)", 376 (int)pjsua_var.buddy[i].uri.slen, 377 pjsua_var.buddy[i].uri.ptr)); 378 } 379 } 380 } 381 382 PJSUA_UNLOCK(); 383 } 384 385 386 /*************************************************************************** 387 * Server subscription. 35 388 */ 36 389 … … 61 414 static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event) 62 415 { 63 pjsua_srv_pres *uapres = pjsip_evsub_get_mod_data(sub, pjsua.mod.id);416 pjsua_srv_pres *uapres; 64 417 65 418 PJ_UNUSED_ARG(event); 66 419 420 PJSUA_LOCK(); 421 422 uapres = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id); 67 423 if (uapres) { 68 424 PJ_LOG(3,(THIS_FILE, "Server subscription to %s is %s", … … 70 426 71 427 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { 72 pjsip_evsub_set_mod_data(sub, pjsua .mod.id, NULL);428 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL); 73 429 pj_list_erase(uapres); 74 430 } 75 431 } 432 433 PJSUA_UNLOCK(); 76 434 } 77 435 … … 81 439 static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata) 82 440 { 83 int acc_i ndex;441 int acc_id; 84 442 pjsua_acc_config *acc_config; 85 443 pjsip_method *req_method = &rdata->msg_info.msg->line.req.method; … … 97 455 /* Incoming SUBSCRIBE: */ 98 456 457 PJSUA_LOCK(); 458 99 459 /* Find which account for the incoming request. */ 100 acc_i ndex= pjsua_acc_find_for_incoming(rdata);101 acc_config = &pjsua .config.acc_config[acc_index];460 acc_id = pjsua_acc_find_for_incoming(rdata); 461 acc_config = &pjsua_var.acc[acc_id].cfg; 102 462 103 463 /* Create UAS dialog: */ … … 109 469 "Unable to create UAS dialog for subscription", 110 470 status); 111 return PJ_FALSE; 471 PJSUA_UNLOCK(); 472 return PJ_TRUE; 112 473 } 113 474 … … 122 483 pjsua_perror(THIS_FILE, "Unable to create server subscription", 123 484 status); 124 return PJ_FALSE; 485 PJSUA_UNLOCK(); 486 return PJ_TRUE; 125 487 } 126 488 … … 136 498 uapres->remote[status] = '\0'; 137 499 138 pjsip_evsub_set_mod_data(sub, pjsua .mod.id, uapres);500 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres); 139 501 140 502 /* Add server subscription to the list: */ 141 pj_list_push_back(&pjsua .acc[acc_index].pres_srv_list, uapres);503 pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres); 142 504 143 505 … … 149 511 pj_list_erase(uapres); 150 512 pjsip_pres_terminate(sub, PJ_FALSE); 513 PJSUA_UNLOCK(); 151 514 return PJ_FALSE; 152 515 } … … 156 519 pj_memset(&pres_status, 0, sizeof(pres_status)); 157 520 pres_status.info_cnt = 1; 158 pres_status.info[0].basic_open = pjsua .acc[acc_index].online_status;159 //Both pjsua .local_uri and pjsua.contact_uri are enclosed in "<" and ">"521 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status; 522 //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">" 160 523 //causing XML parsing to fail. 161 //pres_status.info[0].contact = pjsua .local_uri;524 //pres_status.info[0].contact = pjsua_var.local_uri; 162 525 163 526 pjsip_pres_set_status(sub, &pres_status); … … 174 537 pj_list_erase(uapres); 175 538 pjsip_pres_terminate(sub, PJ_FALSE); 539 PJSUA_UNLOCK(); 176 540 return PJ_FALSE; 177 541 } … … 180 544 /* Done: */ 181 545 546 PJSUA_UNLOCK(); 547 182 548 return PJ_TRUE; 183 549 } 184 550 185 551 552 /* Terminate server subscription for the account */ 553 void pjsua_pres_delete_acc(int acc_id) 554 { 555 pjsua_srv_pres *uapres; 556 557 uapres = pjsua_var.acc[acc_id].pres_srv_list.next; 558 559 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) { 560 561 pjsip_pres_status pres_status; 562 pj_str_t reason = { "noresource", 10 }; 563 pjsip_tx_data *tdata; 564 565 pjsip_pres_get_status(uapres->sub, &pres_status); 566 567 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status; 568 pjsip_pres_set_status(uapres->sub, &pres_status); 569 570 if (pjsip_pres_notify(uapres->sub, 571 PJSIP_EVSUB_STATE_TERMINATED, NULL, 572 &reason, &tdata)==PJ_SUCCESS) 573 { 574 pjsip_pres_send_request(uapres->sub, tdata); 575 } 576 577 uapres = uapres->next; 578 } 579 } 580 581 186 582 /* Refresh subscription (e.g. when our online status has changed) */ 187 static void refresh_server_subscription(int acc_i ndex)583 static void refresh_server_subscription(int acc_id) 188 584 { 189 585 pjsua_srv_pres *uapres; 190 586 191 uapres = pjsua .acc[acc_index].pres_srv_list.next;192 193 while (uapres != &pjsua .acc[acc_index].pres_srv_list) {587 uapres = pjsua_var.acc[acc_id].pres_srv_list.next; 588 589 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) { 194 590 195 591 pjsip_pres_status pres_status; … … 197 593 198 594 pjsip_pres_get_status(uapres->sub, &pres_status); 199 if (pres_status.info[0].basic_open != pjsua .acc[acc_index].online_status) {200 pres_status.info[0].basic_open = pjsua .acc[acc_index].online_status;595 if (pres_status.info[0].basic_open != pjsua_var.acc[acc_id].online_status) { 596 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status; 201 597 pjsip_pres_set_status(uapres->sub, &pres_status); 202 598 203 if (pjsua.quit_flag) { 204 pj_str_t reason = { "noresource", 10 }; 205 if (pjsip_pres_notify(uapres->sub, 206 PJSIP_EVSUB_STATE_TERMINATED, NULL, 207 &reason, &tdata)==PJ_SUCCESS) 208 { 209 pjsip_pres_send_request(uapres->sub, tdata); 210 } 211 } else { 212 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) 213 pjsip_pres_send_request(uapres->sub, tdata); 214 } 599 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) 600 pjsip_pres_send_request(uapres->sub, tdata); 215 601 } 216 602 … … 221 607 222 608 223 /* ************************************************************************** 224 * THE FOLLOWING PART HANDLES CLIENT SUBSCRIPTION 225 * ************************************************************************** 609 /*************************************************************************** 610 * Client subscription. 226 611 */ 227 612 … … 233 618 PJ_UNUSED_ARG(event); 234 619 235 buddy = pjsip_evsub_get_mod_data(sub, pjsua.mod.id); 620 PJSUA_LOCK(); 621 622 buddy = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id); 236 623 if (buddy) { 237 624 PJ_LOG(3,(THIS_FILE, 238 625 "Presence subscription to %.*s is %s", 239 (int)pjsua .config.buddy_uri[buddy->index].slen,240 pjsua .config.buddy_uri[buddy->index].ptr,626 (int)pjsua_var.buddy[buddy->index].uri.slen, 627 pjsua_var.buddy[buddy->index].uri.ptr, 241 628 pjsip_evsub_get_state_name(sub))); 242 629 … … 244 631 buddy->sub = NULL; 245 632 buddy->status.info_cnt = 0; 246 pjsip_evsub_set_mod_data(sub, pjsua .mod.id, NULL);633 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL); 247 634 } 248 635 249 636 /* Call callback */ 250 if (pjsua.cb.on_buddy_state) 251 (*pjsua.cb.on_buddy_state)(buddy->index); 252 } 253 } 637 if (pjsua_var.ua_cfg.cb.on_buddy_state) 638 (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index); 639 } 640 641 PJSUA_UNLOCK(); 642 } 643 644 645 /* Callback when transaction state has changed. */ 646 static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub, 647 pjsip_transaction *tsx, 648 pjsip_event *event) 649 { 650 pjsua_buddy *buddy; 651 pjsip_contact_hdr *contact_hdr; 652 653 PJSUA_LOCK(); 654 655 buddy = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id); 656 if (!buddy) { 657 PJSUA_UNLOCK(); 658 return; 659 } 660 661 /* We only use this to update buddy's Contact, when it's not 662 * set. 663 */ 664 if (buddy->contact.slen != 0) { 665 /* Contact already set */ 666 PJSUA_UNLOCK(); 667 return; 668 } 669 670 /* Only care about 2xx response to outgoing SUBSCRIBE */ 671 if (tsx->status_code/100 != 2 || 672 tsx->role != PJSIP_UAC_ROLE || 673 event->type != PJSIP_EVENT_RX_MSG || 674 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method)!=0) 675 { 676 PJSUA_UNLOCK(); 677 return; 678 } 679 680 /* Find contact header. */ 681 contact_hdr = pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg, 682 PJSIP_H_CONTACT, NULL); 683 if (!contact_hdr) { 684 PJSUA_UNLOCK(); 685 return; 686 } 687 688 buddy->contact.ptr = pj_pool_alloc(pjsua_var.pool, PJSIP_MAX_URL_SIZE); 689 buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR, 690 contact_hdr->uri, 691 buddy->contact.ptr, 692 PJSIP_MAX_URL_SIZE); 693 if (buddy->contact.slen < 0) 694 buddy->contact.slen = 0; 695 696 PJSUA_UNLOCK(); 697 } 698 254 699 255 700 /* Callback called when we receive NOTIFY */ … … 263 708 pjsua_buddy *buddy; 264 709 265 buddy = pjsip_evsub_get_mod_data(sub, pjsua.mod.id); 710 PJSUA_LOCK(); 711 712 buddy = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id); 266 713 if (buddy) { 267 714 /* Update our info. */ … … 277 724 PJ_UNUSED_ARG(res_hdr); 278 725 PJ_UNUSED_ARG(p_body); 726 727 PJSUA_UNLOCK(); 279 728 } 280 729 … … 284 733 { 285 734 &pjsua_evsub_on_state, 286 287 NULL, /* on_tsx_state: don't care about transaction state. */ 735 &pjsua_evsub_on_tsx_state, 288 736 289 737 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless … … 305 753 static void subscribe_buddy_presence(unsigned index) 306 754 { 307 int acc_index; 308 pjsua_acc_config *acc_config; 755 pjsua_buddy *buddy; 756 int acc_id; 757 pjsua_acc *acc; 309 758 pjsip_dialog *dlg; 310 759 pjsip_tx_data *tdata; 311 760 pj_status_t status; 312 761 313 acc_index = pjsua_acc_find_for_outgoing(&pjsua.config.buddy_uri[index]); 314 315 acc_config = &pjsua.config.acc_config[acc_index]; 316 762 buddy = &pjsua_var.buddy[index]; 763 acc_id = pjsua_acc_find_for_outgoing(&buddy->uri); 764 765 acc = &pjsua_var.acc[acc_id]; 766 767 /* Create UAC dialog */ 317 768 status = pjsip_dlg_create_uac( pjsip_ua_instance(), 318 &acc _config->id,319 &acc _config->contact,320 & pjsua.config.buddy_uri[index],769 &acc->cfg.id, 770 &acc->cfg.contact, 771 &buddy->uri, 321 772 NULL, &dlg); 322 773 if (status != PJ_SUCCESS) { … … 326 777 } 327 778 328 if (acc_config->cred_count) { 779 /* Set route-set */ 780 if (!pj_list_empty(&acc->route_set)) { 781 pjsip_dlg_set_route_set(dlg, &acc->route_set); 782 } 783 784 /* Set credentials */ 785 if (acc->cred_cnt) { 329 786 pjsip_auth_clt_set_credentials( &dlg->auth_sess, 330 acc_config->cred_count, 331 acc_config->cred_info); 787 acc->cred_cnt, acc->cred); 332 788 } 333 789 334 790 status = pjsip_pres_create_uac( dlg, &pres_callback, 335 & pjsua.buddies[index].sub);791 &buddy->sub); 336 792 if (status != PJ_SUCCESS) { 337 pjsua .buddies[index].sub = NULL;793 pjsua_var.buddy[index].sub = NULL; 338 794 pjsua_perror(THIS_FILE, "Unable to create presence client", 339 795 status); … … 342 798 } 343 799 344 pjsip_evsub_set_mod_data(pjsua.buddies[index].sub, pjsua.mod.id, 345 &pjsua.buddies[index]); 346 347 status = pjsip_pres_initiate(pjsua.buddies[index].sub, -1, &tdata); 800 pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy); 801 802 status = pjsip_pres_initiate(buddy->sub, -1, &tdata); 348 803 if (status != PJ_SUCCESS) { 349 pjsip_pres_terminate( pjsua.buddies[index].sub, PJ_FALSE);350 pjsua.buddies[index].sub = NULL;804 pjsip_pres_terminate(buddy->sub, PJ_FALSE); 805 buddy->sub = NULL; 351 806 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE", 352 807 status); … … 354 809 } 355 810 356 status = pjsip_pres_send_request( pjsua.buddies[index].sub, tdata);811 status = pjsip_pres_send_request(buddy->sub, tdata); 357 812 if (status != PJ_SUCCESS) { 358 pjsip_pres_terminate( pjsua.buddies[index].sub, PJ_FALSE);359 pjsua.buddies[index].sub = NULL;813 pjsip_pres_terminate(buddy->sub, PJ_FALSE); 814 buddy->sub = NULL; 360 815 pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE", 361 816 status); … … 368 823 static void unsubscribe_buddy_presence(unsigned index) 369 824 { 825 pjsua_buddy *buddy; 370 826 pjsip_tx_data *tdata; 371 827 pj_status_t status; 372 828 373 if (pjsua.buddies[index].sub == NULL)374 return; 375 376 if (pjsip_evsub_get_state(pjsua.buddies[index].sub) == 377 PJSIP_EVSUB_STATE_TERMINATED) 378 {379 pjsua .buddies[index].sub = NULL;380 return; 381 } 382 383 status = pjsip_pres_initiate( pjsua.buddies[index].sub, 0, &tdata);829 buddy = &pjsua_var.buddy[index]; 830 831 if (buddy->sub == NULL) 832 return; 833 834 if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) { 835 pjsua_var.buddy[index].sub = NULL; 836 return; 837 } 838 839 status = pjsip_pres_initiate( buddy->sub, 0, &tdata); 384 840 if (status == PJ_SUCCESS) 385 status = pjsip_pres_send_request( pjsua.buddies[index].sub, tdata );841 status = pjsip_pres_send_request( buddy->sub, tdata ); 386 842 387 843 if (status != PJ_SUCCESS) { 388 389 pjsip_pres_terminate(pjsua.buddies[index].sub, PJ_FALSE); 390 pjsua.buddies[index].sub = NULL; 844 pjsip_pres_terminate(buddy->sub, PJ_FALSE); 845 buddy->sub = NULL; 391 846 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence", 392 847 status); … … 396 851 397 852 /* It does what it says.. */ 398 static void refresh_client_subscription (void)853 static void refresh_client_subscriptions(void) 399 854 { 400 855 unsigned i; 401 856 402 for (i=0; i<pjsua.config.buddy_cnt; ++i) { 403 404 if (pjsua.buddies[i].monitor && !pjsua.buddies[i].sub) { 857 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) { 858 859 if (!pjsua_var.buddy[i].uri.slen) 860 continue; 861 862 if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) { 405 863 subscribe_buddy_presence(i); 406 864 407 } else if (!pjsua .buddies[i].monitor && pjsua.buddies[i].sub) {865 } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) { 408 866 unsubscribe_buddy_presence(i); 409 867 … … 418 876 pj_status_t pjsua_pres_init() 419 877 { 878 unsigned i; 420 879 pj_status_t status; 421 880 422 status = pjsip_endpt_register_module( pjsua .endpt, &mod_pjsua_pres);881 status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres); 423 882 if (status != PJ_SUCCESS) { 424 883 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module", … … 426 885 } 427 886 887 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) { 888 reset_buddy(i); 889 } 890 428 891 return status; 429 892 } 430 893 431 /* 432 * Get buddy count. 433 */ 434 PJ_DEF(unsigned) pjsua_get_buddy_count(void) 435 { 436 return pjsua.config.buddy_cnt; 437 } 438 439 440 /** 441 * Get buddy info. 442 */ 443 PJ_DEF(pj_status_t) pjsua_buddy_get_info(pjsua_buddy_id index, 444 pjsua_buddy_info *info) 445 { 446 pjsua_buddy *buddy; 447 448 PJ_ASSERT_RETURN(index < (int)PJ_ARRAY_SIZE(pjsua.config.buddy_uri), 449 PJ_EINVAL); 450 451 pj_memset(info, 0, sizeof(pjsua_buddy_info)); 452 453 buddy = &pjsua.buddies[index]; 454 info->index = buddy->index; 455 info->is_valid = pjsua.config.buddy_uri[index].slen; 456 if (!info->is_valid) 457 return PJ_SUCCESS; 458 459 info->name = buddy->name; 460 info->display_name = buddy->display; 461 info->host = buddy->host; 462 info->port = buddy->port; 463 info->uri = pjsua.config.buddy_uri[index]; 464 465 if (buddy->sub == NULL || buddy->status.info_cnt==0) { 466 info->status = PJSUA_BUDDY_STATUS_UNKNOWN; 467 info->status_text = pj_str("?"); 468 } else if (pjsua.buddies[index].status.info[0].basic_open) { 469 info->status = PJSUA_BUDDY_STATUS_ONLINE; 470 info->status_text = pj_str("Online"); 471 } else { 472 info->status = PJSUA_BUDDY_STATUS_OFFLINE; 473 info->status_text = pj_str("Offline"); 474 } 475 894 895 /* 896 * Start presence subsystem. 897 */ 898 pj_status_t pjsua_pres_start(void) 899 { 900 /* Nothing to do (is it?) */ 476 901 return PJ_SUCCESS; 477 902 } 478 903 479 904 480 /** 481 * Add new buddy. 482 */ 483 PJ_DEF(pj_status_t) pjsua_buddy_add( const pj_str_t *uri, 484 pjsua_buddy_id *buddy_index) 485 { 486 pjsip_name_addr *url; 487 pjsip_sip_uri *sip_uri; 488 int index; 489 pj_str_t tmp; 490 491 PJ_ASSERT_RETURN(pjsua.config.buddy_cnt <= 492 PJ_ARRAY_SIZE(pjsua.config.buddy_uri), 493 PJ_ETOOMANY); 494 495 /* Find empty slot */ 496 for (index=0; index<PJ_ARRAY_SIZE(pjsua.config.buddy_uri); ++index) { 497 if (pjsua.config.buddy_uri[index].slen == 0) 498 break; 499 } 500 501 /* Expect to find an empty slot */ 502 PJ_ASSERT_RETURN(index < PJ_ARRAY_SIZE(pjsua.config.buddy_uri), 503 PJ_ETOOMANY); 504 505 506 /* Get name and display name for buddy */ 507 pj_strdup_with_null(pjsua.pool, &tmp, uri); 508 url = (pjsip_name_addr*)pjsip_parse_uri(pjsua.pool, tmp.ptr, tmp.slen, 509 PJSIP_PARSE_URI_AS_NAMEADDR); 510 511 if (url == NULL) 512 return PJSIP_EINVALIDURI; 513 514 /* Save URI */ 515 pjsua.config.buddy_uri[index] = tmp; 516 517 sip_uri = (pjsip_sip_uri*) url->uri; 518 pjsua.buddies[index].name = sip_uri->user; 519 pjsua.buddies[index].display = url->display; 520 pjsua.buddies[index].host = sip_uri->host; 521 pjsua.buddies[index].port = sip_uri->port; 522 if (pjsua.buddies[index].port == 0) 523 pjsua.buddies[index].port = 5060; 524 525 if (buddy_index) 526 *buddy_index = index; 527 528 pjsua.config.buddy_cnt++; 529 530 return PJ_SUCCESS; 531 } 532 533 534 535 /** 536 * Delete buddy. 537 */ 538 PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id index) 539 { 540 PJ_ASSERT_RETURN(index < (int)PJ_ARRAY_SIZE(pjsua.config.buddy_uri), 541 PJ_EINVAL); 542 543 if (pjsua.config.buddy_uri[index].slen == 0) 544 return PJ_SUCCESS; 545 546 /* Unsubscribe presence */ 547 pjsua_buddy_subscribe_pres(index, PJ_FALSE); 548 549 /* Remove buddy */ 550 pjsua.config.buddy_uri[index].slen = 0; 551 pjsua.config.buddy_cnt--; 552 553 return PJ_SUCCESS; 554 } 555 556 557 PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id index, 558 pj_bool_t monitor) 559 { 560 pjsua_buddy *buddy; 561 562 PJ_ASSERT_RETURN(index < (int)PJ_ARRAY_SIZE(pjsua.config.buddy_uri), 563 PJ_EINVAL); 564 565 buddy = &pjsua.buddies[index]; 566 buddy->monitor = monitor; 905 /* 906 * Refresh presence subscriptions 907 */ 908 void pjsua_pres_refresh() 909 { 910 unsigned i; 911 912 refresh_client_subscriptions(); 913 914 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { 915 if (pjsua_var.acc[i].valid) 916 refresh_server_subscription(i); 917 } 918 } 919 920 921 /* 922 * Shutdown presence. 923 */ 924 void pjsua_pres_shutdown(void) 925 { 926 unsigned i; 927 928 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { 929 if (!pjsua_var.acc[i].valid) 930 continue; 931 pjsua_pres_delete_acc(i); 932 } 933 934 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) { 935 pjsua_var.buddy[i].monitor = 0; 936 } 937 567 938 pjsua_pres_refresh(); 568 return PJ_SUCCESS; 569 } 570 571 572 PJ_DEF(pj_status_t) pjsua_acc_set_online_status( pjsua_acc_id acc_index, 573 pj_bool_t is_online) 574 { 575 PJ_ASSERT_RETURN(acc_index < (int)PJ_ARRAY_SIZE(pjsua.acc), 576 PJ_EINVAL); 577 PJ_ASSERT_RETURN(pjsua.acc[acc_index].valid, PJ_EINVALIDOP); 578 579 pjsua.acc[acc_index].online_status = is_online; 580 pjsua_pres_refresh(); 581 return PJ_SUCCESS; 582 } 583 584 585 /* 586 * Refresh presence 587 */ 588 PJ_DEF(void) pjsua_pres_refresh() 589 { 590 unsigned i; 591 592 refresh_client_subscription(); 593 594 for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) 595 refresh_server_subscription(i); 596 } 597 598 599 /* 600 * Shutdown presence. 601 */ 602 void pjsua_pres_shutdown(void) 603 { 604 unsigned acc_index; 605 unsigned i; 606 607 for (acc_index=0; acc_index<(int)pjsua.config.acc_cnt; ++acc_index) { 608 pjsua.acc[acc_index].online_status = 0; 609 } 610 611 for (i=0; i<pjsua.config.buddy_cnt; ++i) { 612 pjsua.buddies[i].monitor = 0; 613 } 614 615 pjsua_pres_refresh(); 616 } 617 618 /* 619 * Dump presence status. 620 */ 621 void pjsua_pres_dump(pj_bool_t detail) 622 { 623 unsigned acc_index; 624 unsigned i; 625 626 627 /* 628 * When no detail is required, just dump number of server and client 629 * subscriptions. 630 */ 631 if (detail == PJ_FALSE) { 632 633 int count = 0; 634 635 for (acc_index=0; acc_index < (int)pjsua.config.acc_cnt; ++acc_index) { 636 637 if (!pj_list_empty(&pjsua.acc[acc_index].pres_srv_list)) { 638 struct pjsua_srv_pres *uapres; 639 640 uapres = pjsua.acc[acc_index].pres_srv_list.next; 641 while (uapres != &pjsua.acc[acc_index].pres_srv_list) { 642 ++count; 643 uapres = uapres->next; 644 } 645 } 646 } 647 648 PJ_LOG(3,(THIS_FILE, "Number of server/UAS subscriptions: %d", 649 count)); 650 651 count = 0; 652 653 for (i=0; i<pjsua.config.buddy_cnt; ++i) { 654 if (pjsua.buddies[i].sub) { 655 ++count; 656 } 657 } 658 659 PJ_LOG(3,(THIS_FILE, "Number of client/UAC subscriptions: %d", 660 count)); 661 return; 662 } 663 664 665 /* 666 * Dumping all server (UAS) subscriptions 667 */ 668 PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:")); 669 670 for (acc_index=0; acc_index < (int)pjsua.config.acc_cnt; ++acc_index) { 671 672 PJ_LOG(3,(THIS_FILE, " %.*s", 673 (int)pjsua.config.acc_config[acc_index].id.slen, 674 pjsua.config.acc_config[acc_index].id.ptr)); 675 676 if (pj_list_empty(&pjsua.acc[acc_index].pres_srv_list)) { 677 678 PJ_LOG(3,(THIS_FILE, " - none - ")); 679 680 } else { 681 struct pjsua_srv_pres *uapres; 682 683 uapres = pjsua.acc[acc_index].pres_srv_list.next; 684 while (uapres != &pjsua.acc[acc_index].pres_srv_list) { 685 686 PJ_LOG(3,(THIS_FILE, " %10s %s", 687 pjsip_evsub_get_state_name(uapres->sub), 688 uapres->remote)); 689 690 uapres = uapres->next; 691 } 692 } 693 } 694 695 /* 696 * Dumping all client (UAC) subscriptions 697 */ 698 PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:")); 699 700 if (pjsua.config.buddy_cnt == 0) { 701 702 PJ_LOG(3,(THIS_FILE, " - no buddy list - ")); 703 704 } else { 705 for (i=0; i<pjsua.config.buddy_cnt; ++i) { 706 707 if (pjsua.buddies[i].sub) { 708 PJ_LOG(3,(THIS_FILE, " %10s %.*s", 709 pjsip_evsub_get_state_name(pjsua.buddies[i].sub), 710 (int)pjsua.config.buddy_uri[i].slen, 711 pjsua.config.buddy_uri[i].ptr)); 712 } else { 713 PJ_LOG(3,(THIS_FILE, " %10s %.*s", 714 "(null)", 715 (int)pjsua.config.buddy_uri[i].slen, 716 pjsua.config.buddy_uri[i].ptr)); 717 } 718 } 719 } 720 } 721 939 }
Note: See TracChangeset
for help on using the changeset viewer.