📄 siprtp.c
字号:
/* Attach call data to invite session */
call->inv->mod_data[mod_siprtp.id] = call;
/* Mark start of call */
pj_gettimeofday(&call->start_time);
/* Create initial INVITE request.
* This INVITE request will contain a perfectly good request and
* an SDP body as well.
*/
status = pjsip_inv_invite(call->inv, &tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
/* Send initial INVITE request.
* From now on, the invite session's state will be reported to us
* via the invite session callbacks.
*/
status = pjsip_inv_send_msg(call->inv, tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
return PJ_SUCCESS;
}
/*
* Receive incoming call
*/
static void process_incoming_call(pjsip_rx_data *rdata)
{
unsigned i, options;
struct call *call;
pjsip_dialog *dlg;
pjmedia_sdp_session *sdp;
pjsip_tx_data *tdata;
pj_status_t status;
/* Find free call slot */
for (i=0; i<app.max_calls; ++i) {
if (app.call[i].inv == NULL)
break;
}
if (i == app.max_calls) {
const pj_str_t reason = pj_str("Too many calls");
pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
500, &reason,
NULL, NULL);
return;
}
call = &app.call[i];
/* Verify that we can handle the request. */
options = 0;
status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
app.sip_endpt, &tdata);
if (status != PJ_SUCCESS) {
/*
* No we can't handle the incoming INVITE request.
*/
if (tdata) {
pjsip_response_addr res_addr;
pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
pjsip_endpt_send_response(app.sip_endpt, &res_addr, tdata,
NULL, NULL);
} else {
/* Respond with 500 (Internal Server Error) */
pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 500, NULL,
NULL, NULL);
}
return;
}
/* Create UAS dialog */
status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
&app.local_contact, &dlg);
if (status != PJ_SUCCESS) {
const pj_str_t reason = pj_str("Unable to create dialog");
pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
500, &reason,
NULL, NULL);
return;
}
/* Create SDP */
create_sdp( dlg->pool, call, &sdp);
/* Create UAS invite session */
status = pjsip_inv_create_uas( dlg, rdata, sdp, 0, &call->inv);
if (status != PJ_SUCCESS) {
pjsip_dlg_create_response(dlg, rdata, 500, NULL, &tdata);
pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata);
return;
}
/* Attach call data to invite session */
call->inv->mod_data[mod_siprtp.id] = call;
/* Mark start of call */
pj_gettimeofday(&call->start_time);
/* Create 200 response .*/
status = pjsip_inv_initial_answer(call->inv, rdata, 200,
NULL, NULL, &tdata);
if (status != PJ_SUCCESS) {
status = pjsip_inv_initial_answer(call->inv, rdata,
PJSIP_SC_NOT_ACCEPTABLE,
NULL, NULL, &tdata);
if (status == PJ_SUCCESS)
pjsip_inv_send_msg(call->inv, tdata);
else
pjsip_inv_terminate(call->inv, 500, PJ_FALSE);
return;
}
/* Send the 200 response. */
status = pjsip_inv_send_msg(call->inv, tdata);
PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return);
/* Done */
}
/* Callback to be called when dialog has forked: */
static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
{
PJ_UNUSED_ARG(inv);
PJ_UNUSED_ARG(e);
PJ_TODO( HANDLE_FORKING );
}
/* Callback to be called to handle incoming requests outside dialogs: */
static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
{
/* Ignore strandled ACKs (must not send respone */
if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD)
return PJ_FALSE;
/* Respond (statelessly) any non-INVITE requests with 500 */
if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
pj_str_t reason = pj_str("Unsupported Operation");
pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
500, &reason,
NULL, NULL);
return PJ_TRUE;
}
/* Handle incoming INVITE */
process_incoming_call(rdata);
/* Done */
return PJ_TRUE;
}
/* Callback timer to disconnect call (limiting call duration) */
static void timer_disconnect_call( pj_timer_heap_t *timer_heap,
struct pj_timer_entry *entry)
{
struct call *call = entry->user_data;
PJ_UNUSED_ARG(timer_heap);
entry->id = 0;
hangup_call(call->index);
}
/* Callback to be called when invite session's state has changed: */
static void call_on_state_changed( pjsip_inv_session *inv,
pjsip_event *e)
{
struct call *call = inv->mod_data[mod_siprtp.id];
PJ_UNUSED_ARG(e);
if (!call)
return;
if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
pj_time_val null_time = {0, 0};
if (call->d_timer.id != 0) {
pjsip_endpt_cancel_timer(app.sip_endpt, &call->d_timer);
call->d_timer.id = 0;
}
PJ_LOG(3,(THIS_FILE, "Call #%d disconnected. Reason=%d (%.*s)",
call->index,
inv->cause,
(int)inv->cause_text.slen,
inv->cause_text.ptr));
PJ_LOG(3,(THIS_FILE, "Call #%d statistics:", call->index));
print_call(call->index);
call->inv = NULL;
inv->mod_data[mod_siprtp.id] = NULL;
destroy_call_media(call->index);
call->start_time = null_time;
call->response_time = null_time;
call->connect_time = null_time;
++app.uac_calls;
} else if (inv->state == PJSIP_INV_STATE_CONFIRMED) {
pj_time_val t;
pj_gettimeofday(&call->connect_time);
if (call->response_time.sec == 0)
call->response_time = call->connect_time;
t = call->connect_time;
PJ_TIME_VAL_SUB(t, call->start_time);
PJ_LOG(3,(THIS_FILE, "Call #%d connected in %d ms", call->index,
PJ_TIME_VAL_MSEC(t)));
if (app.duration != 0) {
call->d_timer.id = 1;
call->d_timer.user_data = call;
call->d_timer.cb = &timer_disconnect_call;
t.sec = app.duration;
t.msec = 0;
pjsip_endpt_schedule_timer(app.sip_endpt, &call->d_timer, &t);
}
} else if ( inv->state == PJSIP_INV_STATE_EARLY ||
inv->state == PJSIP_INV_STATE_CONNECTING) {
if (call->response_time.sec == 0)
pj_gettimeofday(&call->response_time);
}
}
/* Utility */
static void app_perror(const char *sender, const char *title,
pj_status_t status)
{
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(3,(sender, "%s: %s [status=%d]", title, errmsg, status));
}
/* Worker thread for SIP */
static int sip_worker_thread(void *arg)
{
PJ_UNUSED_ARG(arg);
while (!app.thread_quit) {
pj_time_val timeout = {0, 10};
pjsip_endpt_handle_events(app.sip_endpt, &timeout);
}
return 0;
}
/* Init application options */
static pj_status_t init_options(int argc, char *argv[])
{
static char ip_addr[32];
static char local_uri[64];
enum { OPT_START,
OPT_APP_LOG_LEVEL, OPT_LOG_FILE,
OPT_A_PT, OPT_A_NAME, OPT_A_CLOCK, OPT_A_BITRATE, OPT_A_PTIME,
OPT_REPORT_FILE };
struct pj_getopt_option long_options[] = {
{ "count", 1, 0, 'c' },
{ "duration", 1, 0, 'd' },
{ "auto-quit", 0, 0, 'q' },
{ "local-port", 1, 0, 'p' },
{ "rtp-port", 1, 0, 'r' },
{ "ip-addr", 1, 0, 'i' },
{ "log-level", 1, 0, 'l' },
{ "app-log-level", 1, 0, OPT_APP_LOG_LEVEL },
{ "log-file", 1, 0, OPT_LOG_FILE },
{ "report-file", 1, 0, OPT_REPORT_FILE },
/* Don't support this anymore, see comments in USAGE above.
{ "a-pt", 1, 0, OPT_A_PT },
{ "a-name", 1, 0, OPT_A_NAME },
{ "a-clock", 1, 0, OPT_A_CLOCK },
{ "a-bitrate", 1, 0, OPT_A_BITRATE },
{ "a-ptime", 1, 0, OPT_A_PTIME },
*/
{ NULL, 0, 0, 0 },
};
int c;
int option_index;
/* Get local IP address for the default IP address */
{
const pj_str_t *hostname;
pj_sockaddr_in tmp_addr;
char *addr;
hostname = pj_gethostname();
pj_sockaddr_in_init(&tmp_addr, hostname, 0);
addr = pj_inet_ntoa(tmp_addr.sin_addr);
pj_ansi_strcpy(ip_addr, addr);
}
/* Init defaults */
app.max_calls = 1;
app.thread_count = 1;
app.sip_port = 5060;
app.rtp_start_port = RTP_START_PORT;
app.local_addr = pj_str(ip_addr);
app.log_level = 5;
app.app_log_level = 3;
app.log_filename = NULL;
/* Default codecs: */
app.audio_codec = audio_codecs[0];
/* Parse options */
pj_optind = 0;
while((c=pj_getopt_long(argc,argv, "c:d:p:r:i:l:q",
long_options, &option_index))!=-1)
{
switch (c) {
case 'c':
app.max_calls = atoi(pj_optarg);
if (app.max_calls < 0 || app.max_calls > MAX_CALLS) {
PJ_LOG(3,(THIS_FILE, "Invalid max calls value %s", pj_optarg));
return 1;
}
break;
case 'd':
app.duration = atoi(pj_optarg);
break;
case 'q':
app.auto_quit = 1;
break;
case 'p':
app.sip_port = atoi(pj_optarg);
break;
case 'r':
app.rtp_start_port = atoi(pj_optarg);
break;
case 'i':
app.local_addr = pj_str(pj_optarg);
break;
case 'l':
app.log_level = atoi(pj_optarg);
break;
case OPT_APP_LOG_LEVEL:
app.app_log_level = atoi(pj_optarg);
break;
case OPT_LOG_FILE:
app.log_filename = pj_optarg;
break;
case OPT_A_PT:
app.audio_codec.pt = atoi(pj_optarg);
break;
case OPT_A_NAME:
app.audio_codec.name = pj_optarg;
break;
case OPT_A_CLOCK:
app.audio_codec.clock_rate = atoi(pj_optarg);
break;
case OPT_A_BITRATE:
app.audio_codec.bit_rate = atoi(pj_optarg);
break;
case OPT_A_PTIME:
app.audio_codec.ptime = atoi(pj_optarg);
break;
case OPT_REPORT_FILE:
app.report_filename = pj_optarg;
break;
default:
puts(USAGE);
return 1;
}
}
/* Check if URL is specified */
if (pj_optind < argc)
app.uri_to_call = pj_str(argv[pj_optind]);
/* Build local URI and contact */
pj_ansi_sprintf( local_uri, "sip:%s:%d", app.local_addr.ptr, app.sip_port);
app.local_uri = pj_str(local_uri);
app.local_contact = app.local_uri;
return PJ_SUCCESS;
}
/*****************************************************************************
* MEDIA STUFFS
*/
/*
* Create SDP session for a call.
*/
static pj_status_t create_sdp( pj_pool_t *pool,
struct call *call,
pjmedia_sdp_session **p_sdp)
{
pj_time_val tv;
pjmedia_sdp_session *sdp;
pjmedia_sdp_media *m;
pjmedia_sdp_attr *attr;
pjmedia_transport_udp_info tpinfo;
struct media_stream *audio = &call->media[0];
PJ_ASSERT_RETURN(pool && p_sdp, PJ_EINVAL);
/* Get transport info */
pjmedia_transport_udp_get_info(audio->transport, &tpinfo);
/* Create and initialize basic SDP session */
sdp = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_session));
pj_gettimeofday(&tv);
sdp->origin.user = pj_str("pjsip-siprtp");
sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
sdp->origin.net_type = pj_str("IN");
sdp->origin.addr_type = pj_str("IP4");
sdp->origin.addr = *pj_gethostname();
sdp->name = pj_str("pjsip");
/* Since we only support one media stream at present, put the
* SDP connection line in the session level.
*/
sdp->conn = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_conn));
sdp->conn->net_type = pj_str("IN");
sdp->conn->addr_type = pj_str("IP4");
sdp->conn->addr = app.local_addr;
/* SDP time and attributes. */
sdp->time.start = sdp->time.stop = 0;
sdp->attr_count = 0;
/* Create media stream 0: */
sdp->media_count = 1;
m = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_media));
sdp->media[0] = m;
/* Standard media info: */
m->desc.media = pj_str("audio");
m->desc.port = pj_ntohs(tpinfo.skinfo.rtp_addr_name.sin_port);
m->desc.port_count = 1;
m->desc.transport = pj_str("RTP/AVP");
/* Add format and rtpmap for each codec. */
m->desc.fmt_count = 1;
m->attr_count = 0;
{
pjmedia_sdp_rtpmap rtpmap;
pjmedia_sdp_attr *attr;
char ptstr[10];
sprintf(ptstr, "%d", app.audio_codec.pt);
pj_strdup2(pool, &m->desc.fmt[0], ptstr);
rtpmap.pt = m->desc.fmt[0];
rtpmap.clock_rate = app.audio_codec.clock_rate;
rtpmap.enc_name = pj_str(app.audio_codec.name);
rtpmap.param.slen = 0;
pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr);
m->attr[m->attr_count++] = attr;
}
/* Add sendrecv attribute. */
attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
attr->name = pj_str("sendrecv");
m->attr[m->attr_count++] = attr;
#if 1
/*
* Add support telephony event
*/
m->desc.fmt[m->desc.fmt_count++] = pj_str("121");
/* Add rtpmap. */
attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
attr->name = pj_str("rtpmap");
attr->value = pj_str("121 telephone-event/8000");
m->attr[m->attr_count++] = attr;
/* Add fmtp */
attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
attr->name = pj_str("fmtp");
attr->value = pj_str("121 0-15");
m->attr[m->attr_count++] = attr;
#endif
/* Done */
*p_sdp = sdp;
return PJ_SUCCESS;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -