📄 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 + -