?? siprtp.c
字號:
/* $Id: siprtp.c 974 2007-02-19 01:13:53Z bennylp $ *//* * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *//* Usage */static const char *USAGE = " PURPOSE: \n"" This program establishes SIP INVITE session and media, and calculate \n"" the media quality (packet lost, jitter, rtt, etc.). Unlike normal \n"" pjmedia applications, this program bypasses all pjmedia stream \n"" framework and transmit encoded RTP packets manually using own thread. \n""\n"" USAGE:\n"" siprtp [options] => to start in server mode\n"" siprtp [options] URL => to start in client mode\n""\n"" Program options:\n"" --count=N, -c Set number of calls to create (default:1) \n"" --duration=SEC, -d Set maximum call duration (default:unlimited) \n"" --auto-quit, -q Quit when calls have been completed (default:no)\n""\n"" Address and ports options:\n"" --local-port=PORT,-p Set local SIP port (default: 5060)\n"" --rtp-port=PORT, -r Set start of RTP port (default: 4000)\n"" --ip-addr=IP, -i Set local IP address to use (otherwise it will\n"" try to determine local IP address from hostname)\n""\n"" Logging Options:\n"" --log-level=N, -l Set log verbosity level (default=5)\n"" --app-log-level=N Set app screen log verbosity (default=3)\n"" --log-file=FILE Write log to file FILE\n"" --report-file=FILE Write report to file FILE\n""\n"/* Don't support this anymore, because codec is properly examined in pjmedia_session_info_from_sdp() function." Codec Options:\n"" --a-pt=PT Set audio payload type to PT (default=0)\n"" --a-name=NAME Set audio codec name to NAME (default=pcmu)\n"" --a-clock=RATE Set audio codec rate to RATE Hz (default=8000Hz)\n"" --a-bitrate=BPS Set audio codec bitrate to BPS (default=64000bps)\n"" --a-ptime=MS Set audio frame time to MS msec (default=20ms)\n"*/;/* Include all headers. */#include <pjsip.h>#include <pjmedia.h>#include <pjmedia-codec.h>#include <pjsip_ua.h>#include <pjsip_simple.h>#include <pjlib-util.h>#include <pjlib.h>#include <stdlib.h>#if PJ_HAS_HIGH_RES_TIMER==0# error "High resolution timer is needed for this sample"#endif#define THIS_FILE "siprtp.c"#define MAX_CALLS 1024#define RTP_START_PORT 4000/* Codec descriptor: */struct codec{ unsigned pt; char* name; unsigned clock_rate; unsigned bit_rate; unsigned ptime; char* description;};/* A bidirectional media stream created when the call is active. */struct media_stream{ /* Static: */ unsigned call_index; /* Call owner. */ unsigned media_index; /* Media index in call. */ pjmedia_transport *transport; /* To send/recv RTP/RTCP */ /* Active? */ pj_bool_t active; /* Non-zero if is in call. */ /* Current stream info: */ pjmedia_stream_info si; /* Current stream info. */ /* More info: */ unsigned clock_rate; /* clock rate */ unsigned samples_per_frame; /* samples per frame */ unsigned bytes_per_frame; /* frame size. */ /* RTP session: */ pjmedia_rtp_session out_sess; /* outgoing RTP session */ pjmedia_rtp_session in_sess; /* incoming RTP session */ /* RTCP stats: */ pjmedia_rtcp_session rtcp; /* incoming RTCP session. */ /* Thread: */ pj_bool_t thread_quit_flag; /* Stop media thread. */ pj_thread_t *thread; /* Media thread. */};/* This is a call structure that is created when the application starts * and only destroyed when the application quits. */struct call{ unsigned index; pjsip_inv_session *inv; unsigned media_count; struct media_stream media[1]; pj_time_val start_time; pj_time_val response_time; pj_time_val connect_time; pj_timer_entry d_timer; /**< Disconnect timer. */};/* Application's global variables */static struct app{ unsigned max_calls; unsigned uac_calls; unsigned duration; pj_bool_t auto_quit; unsigned thread_count; int sip_port; int rtp_start_port; pj_str_t local_addr; pj_str_t local_uri; pj_str_t local_contact; int app_log_level; int log_level; char *log_filename; char *report_filename; struct codec audio_codec; pj_str_t uri_to_call; pj_caching_pool cp; pj_pool_t *pool; pjsip_endpoint *sip_endpt; pj_bool_t thread_quit; pj_thread_t *sip_thread[1]; pjmedia_endpt *med_endpt; struct call call[MAX_CALLS];} app;/* * Prototypes: *//* Callback to be called when SDP negotiation is done in the call: */static void call_on_media_update( pjsip_inv_session *inv, pj_status_t status);/* Callback to be called when invite session's state has changed: */static void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e);/* Callback to be called when dialog has forked: */static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e);/* Callback to be called to handle incoming requests outside dialogs: */static pj_bool_t on_rx_request( pjsip_rx_data *rdata );/* Worker thread prototype */static int sip_worker_thread(void *arg);/* Create SDP for call */static pj_status_t create_sdp( pj_pool_t *pool, struct call *call, pjmedia_sdp_session **p_sdp);/* Hangup call */static void hangup_call(unsigned index);/* Destroy the call's media */static void destroy_call_media(unsigned call_index);/* Destroy media. */static void destroy_media();/* This callback is called by media transport on receipt of RTP packet. */static void on_rx_rtp(void *user_data, const void *pkt, pj_ssize_t size);/* This callback is called by media transport on receipt of RTCP packet. */static void on_rx_rtcp(void *user_data, const void *pkt, pj_ssize_t size);/* Display error */static void app_perror(const char *sender, const char *title, pj_status_t status);/* Print call */static void print_call(int call_index);/* This is a PJSIP module to be registered by application to handle * incoming requests outside any dialogs/transactions. The main purpose * here is to handle incoming INVITE request message, where we will * create a dialog and INVITE session for it. */static pjsip_module mod_siprtp ={ NULL, NULL, /* prev, next. */ { "mod-siprtpapp", 13 }, /* Name. */ -1, /* Id */ PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ NULL, /* unload() */ &on_rx_request, /* on_rx_request() */ NULL, /* on_rx_response() */ NULL, /* on_tx_request. */ NULL, /* on_tx_response() */ NULL, /* on_tsx_state() */};/* Codec constants */struct codec audio_codecs[] = { { 0, "PCMU", 8000, 64000, 20, "G.711 ULaw" }, { 3, "GSM", 8000, 13200, 20, "GSM" }, { 4, "G723", 8000, 6400, 30, "G.723.1" }, { 8, "PCMA", 8000, 64000, 20, "G.711 ALaw" }, { 18, "G729", 8000, 8000, 20, "G.729" },};/* * Init SIP stack */static pj_status_t init_sip(){ unsigned i; pj_status_t status; /* init PJLIB-UTIL: */ status = pjlib_util_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* Must create a pool factory before we can allocate any memory. */ pj_caching_pool_init(&app.cp, &pj_pool_factory_default_policy, 0); /* Create application pool for misc. */ app.pool = pj_pool_create(&app.cp.factory, "app", 1000, 1000, NULL); /* Create the endpoint: */ status = pjsip_endpt_create(&app.cp.factory, pj_gethostname()->ptr, &app.sip_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* Add UDP transport. */ { pj_sockaddr_in addr; pjsip_host_port addrname; pjsip_transport *tp; pj_bzero(&addr, sizeof(addr)); addr.sin_family = PJ_AF_INET; addr.sin_addr.s_addr = 0; addr.sin_port = pj_htons((pj_uint16_t)app.sip_port); if (app.local_addr.slen) { addrname.host = app.local_addr; addrname.port = app.sip_port; status = pj_sockaddr_in_init(&addr, &app.local_addr, (pj_uint16_t)app.sip_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to resolve IP interface", status); return status; } } status = pjsip_udp_transport_start( app.sip_endpt, &addr, (app.local_addr.slen ? &addrname:NULL), 1, &tp); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to start UDP transport", status); return status; } PJ_LOG(3,(THIS_FILE, "SIP UDP listening on %.*s:%d", (int)tp->local_name.host.slen, tp->local_name.host.ptr, tp->local_name.port)); } /* * Init transaction layer. * This will create/initialize transaction hash tables etc. */ status = pjsip_tsx_layer_init_module(app.sip_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* Initialize UA layer. */ status = pjsip_ua_init_module( app.sip_endpt, NULL ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* Init invite session module. */ { pjsip_inv_callback inv_cb; /* Init the callback for INVITE session: */ pj_bzero(&inv_cb, sizeof(inv_cb)); inv_cb.on_state_changed = &call_on_state_changed; inv_cb.on_new_session = &call_on_forked; inv_cb.on_media_update = &call_on_media_update; /* Initialize invite session module: */ status = pjsip_inv_usage_init(app.sip_endpt, &inv_cb); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); } /* Register our module to receive incoming requests. */ status = pjsip_endpt_register_module( app.sip_endpt, &mod_siprtp); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* Init calls */ for (i=0; i<app.max_calls; ++i) app.call[i].index = i; /* Done */ return PJ_SUCCESS;}/* * Destroy SIP */static void destroy_sip(){ unsigned i; app.thread_quit = 1; for (i=0; i<app.thread_count; ++i) { if (app.sip_thread[i]) { pj_thread_join(app.sip_thread[i]); pj_thread_destroy(app.sip_thread[i]); app.sip_thread[i] = NULL; } } if (app.sip_endpt) { pjsip_endpt_destroy(app.sip_endpt); app.sip_endpt = NULL; }}/* * Init media stack. */static pj_status_t init_media(){ unsigned i, count; pj_uint16_t rtp_port; pj_status_t status; /* Initialize media endpoint so that at least error subsystem is properly * initialized. */ status = pjmedia_endpt_create(&app.cp.factory, NULL, 1, &app.med_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* Must register codecs to be supported */#if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0 pjmedia_codec_g711_init(app.med_endpt);#endif /* RTP port counter */ rtp_port = (pj_uint16_t)(app.rtp_start_port & 0xFFFE); /* Init media transport for all calls. */ for (i=0, count=0; i<app.max_calls; ++i, ++count) { unsigned j; /* Create transport for each media in the call */ for (j=0; j<PJ_ARRAY_SIZE(app.call[0].media); ++j) { /* Repeat binding media socket to next port when fails to bind * to current port number. */ int retry; app.call[i].media[j].call_index = i; app.call[i].media[j].media_index = j; status = -1; for (retry=0; retry<100; ++retry,rtp_port+=2) { struct media_stream *m = &app.call[i].media[j]; status = pjmedia_transport_udp_create2(app.med_endpt, "siprtp", &app.local_addr, rtp_port, 0, &m->transport); if (status == PJ_SUCCESS) { rtp_port += 2; break; } } } if (status != PJ_SUCCESS) goto on_error; } /* Done */ return PJ_SUCCESS;on_error: destroy_media(); return status;}/* * Destroy media. */static void destroy_media(){ unsigned i; for (i=0; i<app.max_calls; ++i) { unsigned j; for (j=0; j<PJ_ARRAY_SIZE(app.call[0].media); ++j) { struct media_stream *m = &app.call[i].media[j]; if (m->transport) { pjmedia_transport_close(m->transport); m->transport = NULL; } } } if (app.med_endpt) { pjmedia_endpt_destroy(app.med_endpt); app.med_endpt = NULL; }}/* * Make outgoing call. */static pj_status_t make_call(const pj_str_t *dst_uri){ unsigned i; struct call *call; pjsip_dialog *dlg; pjmedia_sdp_session *sdp; pjsip_tx_data *tdata; pj_status_t status; /* Find unused call slot */ for (i=0; i<app.max_calls; ++i) { if (app.call[i].inv == NULL) break; } if (i == app.max_calls) return PJ_ETOOMANY; call = &app.call[i]; /* Create UAC dialog */ status = pjsip_dlg_create_uac( pjsip_ua_instance(), &app.local_uri, /* local URI */ &app.local_contact, /* local Contact */ dst_uri, /* remote URI */ dst_uri, /* remote target */ &dlg); /* dialog */ if (status != PJ_SUCCESS) { ++app.uac_calls; return status; } /* Create SDP */ create_sdp( dlg->pool, call, &sdp); /* Create the INVITE session. */ status = pjsip_inv_create_uac( dlg, sdp, 0, &call->inv); if (status != PJ_SUCCESS) { pjsip_dlg_terminate(dlg); ++app.uac_calls; return status; }
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -