以下是找到的那个UA使用额sample,不过在pjsip-apps\src\samples还有很多其他的sample。
pjsip-apps\src\samples\simple_pjsua.c
以下是说明,在我看来这个是一个高层次的API的调用,app没有参与SDP的协商,以及媒体面相关的设定
/** * simple_pjsua.c * * This is a very simple but fully featured SIP user agent, with the * following capabilities: * - SIP registration * - Making and receiving call * - Audio/media to sound device. * * Usage: * - To make outgoing call, start simple_pjsua with the URL of remote * destination to contact. * E.g.: * simpleua sip:user@remote * * - Incoming calls will automatically be answered with 200. * * This program will quit once it has completed a single call. */ |
pjsip-apps\src\samples\simpleua.c
这个例子涉及到了媒体面相关的信息,包括SDP的协商等
/** * simpleua.c * * This is a very simple SIP user agent complete with media. The user * agent should do a proper SDP negotiation and start RTP media once * SDP negotiation has completed. * * This program does not register to SIP server. * * Capabilities to be demonstrated here: * - Basic call * - Should support IPv6 (not tested) * - UDP transport at port 5060 (hard coded) * - RTP socket at port 4000 (hard coded) * - proper SDP negotiation * - PCMA/PCMU codec only. * - Audio/media to sound device. * * * Usage: * - To make outgoing call, start simpleua with the URL of remote * destination to contact. * E.g.: * simpleua sip:user@remote * * - Incoming calls will automatically be answered with 180, then 200. * * This program does not disconnect call. * * This program will quit once it has completed a single call. */ |
下面我讲下PJSUA API - High Level Softphone API
http://www.pjsip.org/docs/latest/pjsip/docs/html/group__PJSUA__LIB.htm
1. 涉及到的基本模块
2. PJSUA-APIBasic API
http://www.pjsip.org/docs/latest/pjsip/docs/html/group__PJSUA__LIB__BASE.htm
包括了 DataStructures,Macros,Typedefs,Enumerations,Functions,Detailed Description
在DetailedDescription中讲诉Using PJSUA Library
1. Creating PJSUA
Creating PJSUABefore anything else, application must create PJSUA by calling pjsua_create(). This, among other things, will initialize PJLIB, which is crucial before any PJLIB functions can be called, PJLIB-UTIL, and create a SIP endpoint. |
2. Initializing PJSUA
Initializing PJSUAAfter PJSUA is created, application can initialize PJSUA by calling pjsua_init(). This function takes several optional configuration settings in the argument, if application wants to set them. 这里 pj_status_t pjsua_init ( const pjsua_config * ua_cfg, const pjsua_logging_config * log_cfg, const pjsua_media_config * media_cfg ) Parameters
Returns PJ_SUCCESS on success, or the appropriate error code.
所以这里就有了一段代码的示例,我们需要配置ua_cfg 按照pjsua_config的说明包括了很多,比如max_calls,thread_cnt,dns,stun配置,100rel,sesssion-timer,user_agent,use_srtp,cb(Application callback to receive various event notifications from the library)以上配置可以通过pjsua_config_default(pjsua_config *cfg)获取默认配置cfg,不过这里要说明下的就是cb,这个是app必须要重写的,以下我列出一些基础的cb 一下红色的都是比较基础和重要的,其他的都是增值业务的一些回调 typedef struct pjsua_callback { void (*on_call_state)(pjsua_call_id call_id, pjsip_event *e); void (*on_incoming_call)(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata); void (*on_call_tsx_state)(pjsua_call_id call_id, pjsip_transaction *tsx, pjsip_event *e); --这个是call中的transaction的状态改变的通知 void (*on_call_media_state)(pjsua_call_id call_id); --这个to connect the call's media to sound device,When ICE is used, this callback will also be called to report ICE negotiation failure. void (*on_call_sdp_created)(pjsua_call_id call_id, pjmedia_sdp_session *sdp, pj_pool_t *pool, const pjmedia_sdp_session *rem_sdp); --这个是在创建本地SDP后返回给app的,用来做协议数据的修改,codec的优先级的调整等等。 void (*on_stream_created)(pjsua_call_id call_id, pjmedia_stream *strm, unsigned stream_idx, pjmedia_port **p_port); --会议桥使用 void (*on_dtmf_digit)(pjsua_call_id call_id, int digit);--收到dtmf数字 void (*on_call_transfer_request)(pjsua_call_id call_id, const pj_str_t *dst, pjsip_status_code *code); --收到REFER 消息 void (*on_call_transfer_request2)(pjsua_call_id call_id, const pj_str_t *dst, pjsip_status_code *code, pjsua_call_setting *opt); void (*on_call_transfer_status)(pjsua_call_id call_id, int st_code, const pj_str_t *st_text, pj_bool_t final, pj_bool_t *p_cont); --主动发起transfer的消息的返回状态 void (*on_call_replace_request)(pjsua_call_id call_id, pjsip_rx_data *rdata, int *st_code, pj_str_t *st_text);
void (*on_call_rx_offer)(pjsua_call_id call_id, const pjmedia_sdp_session *offer, void *reserved, pjsip_status_code *code, pjsua_call_setting *opt); -- Notify application when call has received new offer from remote(i.e. re-INVITE/UPDATE with SDP is received). Application can decide to accept/reject the offer by setting the code (default is 200). void (*on_reg_started)(pjsua_acc_id acc_id, pj_bool_t renew); void (*on_reg_state)(pjsua_acc_id acc_id); void (*on_reg_state2)(pjsua_acc_id acc_id, pjsua_reg_info *info); -- * Notify application when registration status has changed. Application may inspect the registration info to get the registration status details.
void (*on_incoming_subscribe)(pjsua_acc_id acc_id, pjsua_srv_pres *srv_pres, pjsua_buddy_id buddy_id, const pj_str_t *from, pjsip_rx_data *rdata, pjsip_status_code *code, pj_str_t *reason, pjsua_msg_data *msg_data); -- Notification when incoming SUBSCRIBE request is received
void (*on_srv_subscribe_state)(pjsua_acc_id acc_id, pjsua_srv_pres *srv_pres, const pj_str_t *remote_uri, pjsip_evsub_state state, pjsip_event *event); -- Notification when server side subscription state has changed. void (*on_buddy_state)(pjsua_buddy_id buddy_id); -- Notify application when the buddy state has changed. void (*on_buddy_evsub_state)(pjsua_buddy_id buddy_id, pjsip_evsub *sub, pjsip_event *event); -- * Notify application when the state of client subscription session associated with a buddy has changed. void (*on_pager)(pjsua_call_id call_id, const pj_str_t *from, const pj_str_t *to, const pj_str_t *contact, const pj_str_t *mime_type, const pj_str_t *body); --IM MESSAGE request pager mode transfer signal void (*on_pager2)(pjsua_call_id call_id, const pj_str_t *from, const pj_str_t *to, const pj_str_t *contact, const pj_str_t *mime_type, const pj_str_t *body, pjsip_rx_data *rdata, pjsua_acc_id acc_id); void (*on_pager_status)(pjsua_call_id call_id, const pj_str_t *to, const pj_str_t *body, void *user_data, pjsip_status_code status, const pj_str_t *reason); --主动发送pager后的状态回调 void (*on_pager_status2)(pjsua_call_id call_id, const pj_str_t *to, const pj_str_t *body, void *user_data, pjsip_status_code status, const pj_str_t *reason, pjsip_tx_data *tdata, pjsip_rx_data *rdata, pjsua_acc_id acc_id); ----主动发送pager后的状态回调和上一个相比参数不一样 void (*on_typing)(pjsua_call_id call_id, const pj_str_t *from, const pj_str_t *to, const pj_str_t *contact, pj_bool_t is_typing); --貌似就是对象真正 输入。。这个不知道怎么用 typing indication (RFC 3994) void (*on_typing2)(pjsua_call_id call_id, const pj_str_t *from, const pj_str_t *to, const pj_str_t *contact, pj_bool_t is_typing, pjsip_rx_data *rdata, pjsua_acc_id acc_id); void (*on_nat_detect)(const pj_stun_nat_detect_result *res); -- Callback when the library has finished performing NAT type detection. pjsip_redirect_op (*on_call_redirected)(pjsua_call_id call_id, const pjsip_uri *target, const pjsip_event *e); --收到redirect处理 void (*on_mwi_state)(pjsua_acc_id acc_id, pjsip_evsub *evsub); --不是很明白 void (*on_mwi_info)(pjsua_acc_id acc_id, pjsua_mwi_info *mwi_info); --不是很明白 pjsip_tp_state_callback on_transport_state;-- * Type of callback to receive transport state notifications, such as transport connected/disconnected. Application may shutdown the transport in this callback. pjsua_med_tp_state_cb on_call_media_transport_state; -- * Type of callback to be called when media transport state is changed. @return The callback must return PJ_SUCCESS at the moment. void (*on_ice_transport_error)(int index, pj_ice_strans_op op, pj_status_t status, void *param); -- * This callback is called to report error in ICE media transport. Currently it is used to report TURN Refresh error. pj_status_t (*on_snd_dev_operation)(int operation); --* Callback when the sound device is about to be opened or closed. This callback will be called even when null sound device or no sound device is configured by the application (i.e. the #pjsua_set_null_snd_dev() and #pjsua_set_no_snd_dev() APIs). void (*on_call_media_event)(pjsua_call_id call_id, unsigned med_idx, pjmedia_event *event); --Notification about media events such as video notifications. This callback will most likely be called from media threads, thus application must not perform heavy processing in this callback. pjmedia_transport* (*on_create_media_transport)(pjsua_call_id call_id, unsigned media_idx, pjmedia_transport *base_tp, unsigned flags); -- * This callback is called when a new call is created. The library has created a media transport for the call, and it is provided as the \a base_tp argument of this callback. Upon returning, the callback must return an instance of media transport to be used by the call. void (*on_acc_find_for_incoming)(const pjsip_rx_data *rdata, pjsua_acc_id* acc_id); --that currently the incoming messages requiring account assignment are INVITE, MESSAGE, SUBSCRIBE, and unsolicited NOTIFY. 对上面的这些初始消息分配的acc_id,进行重写
|
3. PJSUA-LIB Initialization (in C)
#include #define THIS_FILE __FILE__ static pj_status_t app_init(void) { pjsua_config ua_cfg; pjsua_logging_config log_cfg; pjsua_media_config media_cfg; pj_status_t status; // Must create pjsua before anything else! status = pjsua_create(); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error initializing pjsua", status); return status; } // Initialize configs with default settings. pjsua_config_default(&ua_cfg); pjsua_logging_config_default(&log_cfg); pjsua_media_config_default(&media_cfg); // At the very least, application would want to override // the call callbacks in pjsua_config: ua_cfg.cb.on_incoming_call = ...cb ua_cfg.cb.on_call_state = ..cb ... // Customize other settings (or initialize them from application specific // configuration file): ... // Initialize pjsua status = pjsua_init(&ua_cfg, &log_cfg, &media_cfg); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error initializing pjsua", status); return status; } . ... } |
4. pjsua_transport_create() 设置UDP,TCP,TLS
create SIP transport with pjsua_transport_create(). Application would to call pjsua_transport_create() for each transport types that it wants to support (for example, UDP, TCP, and TLS). Please see PJSUA-API Signaling Transport section for more info. |
5. pjsua_acc_add()
create one or more SIP accounts with pjsua_acc_add() or pjsua_acc_add_local(). The SIP account is used for registering with the SIP server, if any. Please see PJSUA-API Accounts Management for more info. |
6. pjsua_buddy_add()
add one or more buddies with pjsua_buddy_add(). Please see PJSUA-API Buddy, Presence, and Instant Messaging section for more info. |
7. Configure media 包括sounddevice, codec settings, and other media settings
optionally configure the sound device, codec settings, and other media settings. Please see PJSUA-API Media Manipulation for more info. |
8. Starting PJSUA
在init完成之后需要调用 pjsua_start()启动pjsua,调用后会对相关的配置进行检查,会返回成功和失败。
这里要说明的是配置修改都是及时生效的,包括applicationmay add, modify, or delete accounts, buddies, or change media settings
C Example for Starting PJSUA
Sample code:
static pj_status_t app_run(void) { pj_status_t status; // Start pjsua status = pjsua_start(); if (status != PJ_SUCCESS) { pjsua_destroy(); pjsua_perror(THIS_FILE, "Error starting pjsua", status); return status; } // Run application loop while (1) { char choice[10]; printf("Select menu: "); fgets(choice, sizeof(choice), stdin); ... } } |
9. 增加on_incoming_call,这里处理收到初始INVITE的消息的处理,
static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata) { pjsua_call_info ci;
PJ_UNUSED_ARG(acc_id); PJ_UNUSED_ARG(rdata);
pjsua_call_get_info(call_id, &ci);
PJ_LOG(3,(THIS_FILE, "Incoming call from %.*s!!", (int)ci.remote_info.slen, ci.remote_info.ptr));
//cxj add pjsua_call_answer(call_id, 180, NULL, NULL); --我加了振铃 [NSThread sleepForTimeInterval:10.0f];
/* Automatically answer incoming calls with 200/OK */ pjsua_call_answer(call_id, 200, NULL, NULL);
} 以下是pjsua_call_answer,这里的code可以是100-699的数,msg_data这个值应该是增加的头,不过这里不知道怎么用, PJ_DECL(pj_status_t) pjsua_call_answer(pjsua_call_id call_id, unsigned code, const pj_str_t *reason, const pjsua_msg_data *msg_data);
|
10. 增加on_call_state 这个是信令层呼叫状态
static void on_call_state(pjsua_call_id call_id, pjsip_event *e) { pjsua_call_info ci;
PJ_UNUSED_ARG(e);
pjsua_call_get_info(call_id, &ci); PJ_LOG(3,(THIS_FILE, "Call %d state=%.*s", call_id, (int)ci.state_text.slen, ci.state_text.ptr)); } 这里打印出呼叫状态的值,用于显示,就是calling、incoming、early、connecting、connected、confirmed /** Call state */ pjsip_inv_state state; /** Text describing the state */ pj_str_t state_text; /** * This enumeration describes invite session state. */ typedef enum pjsip_inv_state { PJSIP_INV_STATE_NULL, /**< Before INVITE is sent or received */ PJSIP_INV_STATE_CALLING, /**< After INVITE is sent */ PJSIP_INV_STATE_INCOMING, /**< After INVITE is received. */ PJSIP_INV_STATE_EARLY, /**< After response with To tag. */ PJSIP_INV_STATE_CONNECTING, /**< After 2xx is sent/received. */ PJSIP_INV_STATE_CONFIRMED, /**< After ACK is sent/received. */ PJSIP_INV_STATE_DISCONNECTED, /**< Session is terminated. */ } pjsip_inv_state; |
11. 增加on_call_media_state媒体状态
static void on_call_media_state(pjsua_call_id call_id) { pjsua_call_info ci;
pjsua_call_get_info(call_id, &ci);
if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE) { // When media is active, connect call to sound device. pjsua_conf_connect(ci.conf_slot, 0); pjsua_conf_connect(0, ci.conf_slot); } } |