BLOG | DOCUMENTATION | TRAC

Home --> Documentations --> PJSIP Reference

Samples: Simple UA

This is a very simple SIP User Agent application that only use PJSIP (without PJSIP-UA). It's able to make and receive call, and play media to the sound device.

00001 /* $Id: simpleua.c 4051 2012-04-13 08:16:30Z ming $ */
00002 /* 
00003  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
00004  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
00019  */
00020 
00021 
00054 /* Include all headers. */
00055 #include <pjsip.h>
00056 #include <pjmedia.h>
00057 #include <pjmedia-codec.h>
00058 #include <pjsip_ua.h>
00059 #include <pjsip_simple.h>
00060 #include <pjlib-util.h>
00061 #include <pjlib.h>
00062 
00063 /* For logging purpose. */
00064 #define THIS_FILE   "simpleua.c"
00065 
00066 #include "util.h"
00067 
00068 
00069 /* Settings */
00070 #define AF              pj_AF_INET() /* Change to pj_AF_INET6() for IPv6.
00071                                       * PJ_HAS_IPV6 must be enabled and
00072                                       * your system must support IPv6.  */
00073 #if 0
00074 #define SIP_PORT        5080         /* Listening SIP port              */
00075 #define RTP_PORT        5000         /* RTP port                        */
00076 #else
00077 #define SIP_PORT        5060         /* Listening SIP port              */
00078 #define RTP_PORT        4000         /* RTP port                        */
00079 #endif
00080 
00081 #define MAX_MEDIA_CNT   2            /* Media count, set to 1 for audio
00082                                       * only or 2 for audio and video   */
00083 
00084 /*
00085  * Static variables.
00086  */
00087 
00088 static pj_bool_t             g_complete;    /* Quit flag.               */
00089 static pjsip_endpoint       *g_endpt;       /* SIP endpoint.            */
00090 static pj_caching_pool       cp;            /* Global pool factory.     */
00091 
00092 static pjmedia_endpt        *g_med_endpt;   /* Media endpoint.          */
00093 
00094 static pjmedia_transport_info g_med_tpinfo[MAX_MEDIA_CNT]; 
00095                                             /* Socket info for media    */
00096 static pjmedia_transport    *g_med_transport[MAX_MEDIA_CNT];
00097                                             /* Media stream transport   */
00098 static pjmedia_sock_info     g_sock_info[MAX_MEDIA_CNT];  
00099                                             /* Socket info array        */
00100 
00101 /* Call variables: */
00102 static pjsip_inv_session    *g_inv;         /* Current invite session.  */
00103 static pjmedia_stream       *g_med_stream;  /* Call's audio stream.     */
00104 static pjmedia_snd_port     *g_snd_port;    /* Sound device.            */
00105 
00106 #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
00107 static pjmedia_vid_stream   *g_med_vstream; /* Call's video stream.     */
00108 static pjmedia_vid_port     *g_vid_capturer;/* Call's video capturer.   */
00109 static pjmedia_vid_port     *g_vid_renderer;/* Call's video renderer.   */
00110 #endif  /* PJMEDIA_HAS_VIDEO */
00111 
00112 /*
00113  * Prototypes:
00114  */
00115 
00116 /* Callback to be called when SDP negotiation is done in the call: */
00117 static void call_on_media_update( pjsip_inv_session *inv,
00118                                   pj_status_t status);
00119 
00120 /* Callback to be called when invite session's state has changed: */
00121 static void call_on_state_changed( pjsip_inv_session *inv, 
00122                                    pjsip_event *e);
00123 
00124 /* Callback to be called when dialog has forked: */
00125 static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e);
00126 
00127 /* Callback to be called to handle incoming requests outside dialogs: */
00128 static pj_bool_t on_rx_request( pjsip_rx_data *rdata );
00129 
00130 
00131 
00132 
00133 /* This is a PJSIP module to be registered by application to handle
00134  * incoming requests outside any dialogs/transactions. The main purpose
00135  * here is to handle incoming INVITE request message, where we will
00136  * create a dialog and INVITE session for it.
00137  */
00138 static pjsip_module mod_simpleua =
00139 {
00140     NULL, NULL,                     /* prev, next.              */
00141     { "mod-simpleua", 12 },         /* Name.                    */
00142     -1,                             /* Id                       */
00143     PJSIP_MOD_PRIORITY_APPLICATION, /* Priority                 */
00144     NULL,                           /* load()                   */
00145     NULL,                           /* start()                  */
00146     NULL,                           /* stop()                   */
00147     NULL,                           /* unload()                 */
00148     &on_rx_request,                 /* on_rx_request()          */
00149     NULL,                           /* on_rx_response()         */
00150     NULL,                           /* on_tx_request.           */
00151     NULL,                           /* on_tx_response()         */
00152     NULL,                           /* on_tsx_state()           */
00153 };
00154 
00155 
00156 /* Notification on incoming messages */
00157 static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
00158 {
00159     PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
00160                          "%.*s\n"
00161                          "--end msg--",
00162                          rdata->msg_info.len,
00163                          pjsip_rx_data_get_info(rdata),
00164                          rdata->tp_info.transport->type_name,
00165                          rdata->pkt_info.src_name,
00166                          rdata->pkt_info.src_port,
00167                          (int)rdata->msg_info.len,
00168                          rdata->msg_info.msg_buf));
00169     
00170     /* Always return false, otherwise messages will not get processed! */
00171     return PJ_FALSE;
00172 }
00173 
00174 /* Notification on outgoing messages */
00175 static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
00176 {
00177     
00178     /* Important note:
00179      *  tp_info field is only valid after outgoing messages has passed
00180      *  transport layer. So don't try to access tp_info when the module
00181      *  has lower priority than transport layer.
00182      */
00183 
00184     PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
00185                          "%.*s\n"
00186                          "--end msg--",
00187                          (tdata->buf.cur - tdata->buf.start),
00188                          pjsip_tx_data_get_info(tdata),
00189                          tdata->tp_info.transport->type_name,
00190                          tdata->tp_info.dst_name,
00191                          tdata->tp_info.dst_port,
00192                          (int)(tdata->buf.cur - tdata->buf.start),
00193                          tdata->buf.start));
00194 
00195     /* Always return success, otherwise message will not get sent! */
00196     return PJ_SUCCESS;
00197 }
00198 
00199 /* The module instance. */
00200 static pjsip_module msg_logger = 
00201 {
00202     NULL, NULL,                         /* prev, next.          */
00203     { "mod-msg-log", 13 },              /* Name.                */
00204     -1,                                 /* Id                   */
00205     PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority            */
00206     NULL,                               /* load()               */
00207     NULL,                               /* start()              */
00208     NULL,                               /* stop()               */
00209     NULL,                               /* unload()             */
00210     &logging_on_rx_msg,                 /* on_rx_request()      */
00211     &logging_on_rx_msg,                 /* on_rx_response()     */
00212     &logging_on_tx_msg,                 /* on_tx_request.       */
00213     &logging_on_tx_msg,                 /* on_tx_response()     */
00214     NULL,                               /* on_tsx_state()       */
00215 
00216 };
00217 
00218 
00219 /*
00220  * main()
00221  *
00222  * If called with argument, treat argument as SIP URL to be called.
00223  * Otherwise wait for incoming calls.
00224  */
00225 int main(int argc, char *argv[])
00226 {
00227     pj_pool_t *pool = NULL;
00228     pj_status_t status;
00229     unsigned i;
00230 
00231     /* Must init PJLIB first: */
00232     status = pj_init();
00233     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00234 
00235     pj_log_set_level(5);
00236 
00237     /* Then init PJLIB-UTIL: */
00238     status = pjlib_util_init();
00239     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00240 
00241 
00242     /* Must create a pool factory before we can allocate any memory. */
00243     pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
00244 
00245 
00246     /* Create global endpoint: */
00247     {
00248         const pj_str_t *hostname;
00249         const char *endpt_name;
00250 
00251         /* Endpoint MUST be assigned a globally unique name.
00252          * The name will be used as the hostname in Warning header.
00253          */
00254 
00255         /* For this implementation, we'll use hostname for simplicity */
00256         hostname = pj_gethostname();
00257         endpt_name = hostname->ptr;
00258 
00259         /* Create the endpoint: */
00260 
00261         status = pjsip_endpt_create(&cp.factory, endpt_name, 
00262                                     &g_endpt);
00263         PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00264     }
00265 
00266 
00267     /* 
00268      * Add UDP transport, with hard-coded port 
00269      * Alternatively, application can use pjsip_udp_transport_attach() to
00270      * start UDP transport, if it already has an UDP socket (e.g. after it
00271      * resolves the address with STUN).
00272      */
00273     {
00274         pj_sockaddr addr;
00275 
00276         pj_sockaddr_init(AF, &addr, NULL, (pj_uint16_t)SIP_PORT);
00277         
00278         if (AF == pj_AF_INET()) {
00279             status = pjsip_udp_transport_start( g_endpt, &addr.ipv4, NULL, 
00280                                                 1, NULL);
00281         } else if (AF == pj_AF_INET6()) {
00282             status = pjsip_udp_transport_start6(g_endpt, &addr.ipv6, NULL,
00283                                                 1, NULL);
00284         } else {
00285             status = PJ_EAFNOTSUP;
00286         }
00287 
00288         if (status != PJ_SUCCESS) {
00289             app_perror(THIS_FILE, "Unable to start UDP transport", status);
00290             return 1;
00291         }
00292     }
00293 
00294 
00295     /* 
00296      * Init transaction layer.
00297      * This will create/initialize transaction hash tables etc.
00298      */
00299     status = pjsip_tsx_layer_init_module(g_endpt);
00300     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00301 
00302 
00303     /* 
00304      * Initialize UA layer module.
00305      * This will create/initialize dialog hash tables etc.
00306      */
00307     status = pjsip_ua_init_module( g_endpt, NULL );
00308     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00309 
00310 
00311     /* 
00312      * Init invite session module.
00313      * The invite session module initialization takes additional argument,
00314      * i.e. a structure containing callbacks to be called on specific
00315      * occurence of events.
00316      *
00317      * The on_state_changed and on_new_session callbacks are mandatory.
00318      * Application must supply the callback function.
00319      *
00320      * We use on_media_update() callback in this application to start
00321      * media transmission.
00322      */
00323     {
00324         pjsip_inv_callback inv_cb;
00325 
00326         /* Init the callback for INVITE session: */
00327         pj_bzero(&inv_cb, sizeof(inv_cb));
00328         inv_cb.on_state_changed = &call_on_state_changed;
00329         inv_cb.on_new_session = &call_on_forked;
00330         inv_cb.on_media_update = &call_on_media_update;
00331 
00332         /* Initialize invite session module:  */
00333         status = pjsip_inv_usage_init(g_endpt, &inv_cb);
00334         PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00335     }
00336 
00337     /* Initialize 100rel support */
00338     status = pjsip_100rel_init_module(g_endpt);
00339     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00340 
00341     /*
00342      * Register our module to receive incoming requests.
00343      */
00344     status = pjsip_endpt_register_module( g_endpt, &mod_simpleua);
00345     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00346 
00347     /*
00348      * Register message logger module.
00349      */
00350     status = pjsip_endpt_register_module( g_endpt, &msg_logger);
00351     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00352 
00353 
00354     /* 
00355      * Initialize media endpoint.
00356      * This will implicitly initialize PJMEDIA too.
00357      */
00358 #if PJ_HAS_THREADS
00359     status = pjmedia_endpt_create(&cp.factory, NULL, 1, &g_med_endpt);
00360 #else
00361     status = pjmedia_endpt_create(&cp.factory, 
00362                                   pjsip_endpt_get_ioqueue(g_endpt), 
00363                                   0, &g_med_endpt);
00364 #endif
00365     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00366 
00367     /* 
00368      * Add PCMA/PCMU codec to the media endpoint. 
00369      */
00370 #if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0
00371     status = pjmedia_codec_g711_init(g_med_endpt);
00372     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00373 #endif
00374 
00375 
00376 #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
00377     /* Init video subsystem */
00378     pool = pjmedia_endpt_create_pool(g_med_endpt, "Video subsystem", 512, 512);
00379     status = pjmedia_video_format_mgr_create(pool, 64, 0, NULL);
00380     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00381     status = pjmedia_converter_mgr_create(pool, NULL);
00382     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00383     status = pjmedia_vid_codec_mgr_create(pool, NULL);
00384     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00385     status = pjmedia_vid_dev_subsys_init(&cp.factory);
00386     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00387 
00388 #  if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC!=0
00389     /* Init ffmpeg video codecs */
00390     status = pjmedia_codec_ffmpeg_vid_init(NULL, &cp.factory);
00391     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00392 #  endif  /* PJMEDIA_HAS_FFMPEG_VID_CODEC */
00393 
00394 #endif  /* PJMEDIA_HAS_VIDEO */
00395     
00396     /* 
00397      * Create media transport used to send/receive RTP/RTCP socket.
00398      * One media transport is needed for each call. Application may
00399      * opt to re-use the same media transport for subsequent calls.
00400      */
00401     for (i = 0; i < PJ_ARRAY_SIZE(g_med_transport); ++i) {
00402         status = pjmedia_transport_udp_create3(g_med_endpt, AF, NULL, NULL, 
00403                                                RTP_PORT + i*2, 0, 
00404                                                &g_med_transport[i]);
00405         if (status != PJ_SUCCESS) {
00406             app_perror(THIS_FILE, "Unable to create media transport", status);
00407             return 1;
00408         }
00409 
00410         /* 
00411          * Get socket info (address, port) of the media transport. We will
00412          * need this info to create SDP (i.e. the address and port info in
00413          * the SDP).
00414          */
00415         pjmedia_transport_info_init(&g_med_tpinfo[i]);
00416         pjmedia_transport_get_info(g_med_transport[i], &g_med_tpinfo[i]);
00417 
00418         pj_memcpy(&g_sock_info[i], &g_med_tpinfo[i].sock_info,
00419                   sizeof(pjmedia_sock_info));
00420     }
00421 
00422     /*
00423      * If URL is specified, then make call immediately.
00424      */
00425     if (argc > 1) {
00426         pj_sockaddr hostaddr;
00427         char hostip[PJ_INET6_ADDRSTRLEN+2];
00428         char temp[80];
00429         pj_str_t dst_uri = pj_str(argv[1]);
00430         pj_str_t local_uri;
00431         pjsip_dialog *dlg;
00432         pjmedia_sdp_session *local_sdp;
00433         pjsip_tx_data *tdata;
00434 
00435         if (pj_gethostip(AF, &hostaddr) != PJ_SUCCESS) {
00436             app_perror(THIS_FILE, "Unable to retrieve local host IP", status);
00437             return 1;
00438         }
00439         pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2);
00440 
00441         pj_ansi_sprintf(temp, "<sip:simpleuac@%s:%d>", 
00442                         hostip, SIP_PORT);
00443         local_uri = pj_str(temp);
00444 
00445         /* Create UAC dialog */
00446         status = pjsip_dlg_create_uac( pjsip_ua_instance(), 
00447                                        &local_uri,  /* local URI */
00448                                        &local_uri,  /* local Contact */
00449                                        &dst_uri,    /* remote URI */
00450                                        &dst_uri,    /* remote target */
00451                                        &dlg);       /* dialog */
00452         if (status != PJ_SUCCESS) {
00453             app_perror(THIS_FILE, "Unable to create UAC dialog", status);
00454             return 1;
00455         }
00456 
00457         /* If we expect the outgoing INVITE to be challenged, then we should
00458          * put the credentials in the dialog here, with something like this:
00459          *
00460             {
00461                 pjsip_cred_info cred[1];
00462 
00463                 cred[0].realm     = pj_str("sip.server.realm");
00464                 cred[0].scheme    = pj_str("digest");
00465                 cred[0].username  = pj_str("theuser");
00466                 cred[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
00467                 cred[0].data      = pj_str("thepassword");
00468 
00469                 pjsip_auth_clt_set_credentials( &dlg->auth_sess, 1, cred);
00470             }
00471          *
00472          */
00473 
00474 
00475         /* Get the SDP body to be put in the outgoing INVITE, by asking
00476          * media endpoint to create one for us.
00477          */
00478         status = pjmedia_endpt_create_sdp( g_med_endpt,     /* the media endpt  */
00479                                            dlg->pool,       /* pool.            */
00480                                            MAX_MEDIA_CNT,   /* # of streams     */
00481                                            g_sock_info,     /* RTP sock info    */
00482                                            &local_sdp);     /* the SDP result   */
00483         PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00484 
00485 
00486 
00487         /* Create the INVITE session, and pass the SDP returned earlier
00488          * as the session's initial capability.
00489          */
00490         status = pjsip_inv_create_uac( dlg, local_sdp, 0, &g_inv);
00491         PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00492 
00493         /* If we want the initial INVITE to travel to specific SIP proxies,
00494          * then we should put the initial dialog's route set here. The final
00495          * route set will be updated once a dialog has been established.
00496          * To set the dialog's initial route set, we do it with something
00497          * like this:
00498          *
00499             {
00500                 pjsip_route_hdr route_set;
00501                 pjsip_route_hdr *route;
00502                 const pj_str_t hname = { "Route", 5 };
00503                 char *uri = "sip:proxy.server;lr";
00504 
00505                 pj_list_init(&route_set);
00506 
00507                 route = pjsip_parse_hdr( dlg->pool, &hname, 
00508                                          uri, strlen(uri),
00509                                          NULL);
00510                 PJ_ASSERT_RETURN(route != NULL, 1);
00511                 pj_list_push_back(&route_set, route);
00512 
00513                 pjsip_dlg_set_route_set(dlg, &route_set);
00514             }
00515          *
00516          * Note that Route URI SHOULD have an ";lr" parameter!
00517          */
00518 
00519         /* Create initial INVITE request.
00520          * This INVITE request will contain a perfectly good request and 
00521          * an SDP body as well.
00522          */
00523         status = pjsip_inv_invite(g_inv, &tdata);
00524         PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00525 
00526 
00527 
00528         /* Send initial INVITE request. 
00529          * From now on, the invite session's state will be reported to us
00530          * via the invite session callbacks.
00531          */
00532         status = pjsip_inv_send_msg(g_inv, tdata);
00533         PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00534 
00535 
00536     } else {
00537 
00538         /* No URL to make call to */
00539 
00540         PJ_LOG(3,(THIS_FILE, "Ready to accept incoming calls..."));
00541     }
00542 
00543 
00544     /* Loop until one call is completed */
00545     for (;!g_complete;) {
00546         pj_time_val timeout = {0, 10};
00547         pjsip_endpt_handle_events(g_endpt, &timeout);
00548     }
00549 
00550     /* On exit, dump current memory usage: */
00551     dump_pool_usage(THIS_FILE, &cp);
00552 
00553     /* Destroy audio ports. Destroy the audio port first
00554      * before the stream since the audio port has threads
00555      * that get/put frames to the stream.
00556      */
00557     if (g_snd_port)
00558         pjmedia_snd_port_destroy(g_snd_port);
00559 
00560 #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
00561     /* Destroy video ports */
00562     if (g_vid_capturer)
00563         pjmedia_vid_port_destroy(g_vid_capturer);
00564     if (g_vid_renderer)
00565         pjmedia_vid_port_destroy(g_vid_renderer);
00566 #endif
00567 
00568     /* Destroy streams */
00569     if (g_med_stream)
00570         pjmedia_stream_destroy(g_med_stream);
00571 #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
00572     if (g_med_vstream)
00573         pjmedia_vid_stream_destroy(g_med_vstream);
00574 
00575     /* Deinit ffmpeg codec */
00576 #   if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC!=0
00577     pjmedia_codec_ffmpeg_vid_deinit();
00578 #   endif
00579 
00580 #endif
00581 
00582     /* Destroy media transports */
00583     for (i = 0; i < MAX_MEDIA_CNT; ++i) {
00584         if (g_med_transport[i])
00585             pjmedia_transport_close(g_med_transport[i]);
00586     }
00587 
00588     /* Deinit pjmedia endpoint */
00589     if (g_med_endpt)
00590         pjmedia_endpt_destroy(g_med_endpt);
00591 
00592     /* Deinit pjsip endpoint */
00593     if (g_endpt)
00594         pjsip_endpt_destroy(g_endpt);
00595 
00596     /* Release pool */
00597     if (pool)
00598         pj_pool_release(pool);
00599 
00600     return 0;
00601 }
00602 
00603 
00604 
00605 /*
00606  * Callback when INVITE session state has changed.
00607  * This callback is registered when the invite session module is initialized.
00608  * We mostly want to know when the invite session has been disconnected,
00609  * so that we can quit the application.
00610  */
00611 static void call_on_state_changed( pjsip_inv_session *inv, 
00612                                    pjsip_event *e)
00613 {
00614     PJ_UNUSED_ARG(e);
00615 
00616     if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
00617 
00618         PJ_LOG(3,(THIS_FILE, "Call DISCONNECTED [reason=%d (%s)]", 
00619                   inv->cause,
00620                   pjsip_get_status_text(inv->cause)->ptr));
00621 
00622         PJ_LOG(3,(THIS_FILE, "One call completed, application quitting..."));
00623         g_complete = 1;
00624 
00625     } else {
00626 
00627         PJ_LOG(3,(THIS_FILE, "Call state changed to %s", 
00628                   pjsip_inv_state_name(inv->state)));
00629 
00630     }
00631 }
00632 
00633 
00634 /* This callback is called when dialog has forked. */
00635 static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
00636 {
00637     /* To be done... */
00638     PJ_UNUSED_ARG(inv);
00639     PJ_UNUSED_ARG(e);
00640 }
00641 
00642 
00643 /*
00644  * Callback when incoming requests outside any transactions and any
00645  * dialogs are received. We're only interested to hande incoming INVITE
00646  * request, and we'll reject any other requests with 500 response.
00647  */
00648 static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
00649 {
00650     pj_sockaddr hostaddr;
00651     char temp[80], hostip[PJ_INET6_ADDRSTRLEN];
00652     pj_str_t local_uri;
00653     pjsip_dialog *dlg;
00654     pjmedia_sdp_session *local_sdp;
00655     pjsip_tx_data *tdata;
00656     unsigned options = 0;
00657     pj_status_t status;
00658 
00659 
00660     /* 
00661      * Respond (statelessly) any non-INVITE requests with 500 
00662      */
00663     if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
00664 
00665         if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
00666             pj_str_t reason = pj_str("Simple UA unable to handle "
00667                                      "this request");
00668 
00669             pjsip_endpt_respond_stateless( g_endpt, rdata, 
00670                                            500, &reason,
00671                                            NULL, NULL);
00672         }
00673         return PJ_TRUE;
00674     }
00675 
00676 
00677     /*
00678      * Reject INVITE if we already have an INVITE session in progress.
00679      */
00680     if (g_inv) {
00681 
00682         pj_str_t reason = pj_str("Another call is in progress");
00683 
00684         pjsip_endpt_respond_stateless( g_endpt, rdata, 
00685                                        500, &reason,
00686                                        NULL, NULL);
00687         return PJ_TRUE;
00688 
00689     }
00690 
00691     /* Verify that we can handle the request. */
00692     status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
00693                                       g_endpt, NULL);
00694     if (status != PJ_SUCCESS) {
00695 
00696         pj_str_t reason = pj_str("Sorry Simple UA can not handle this INVITE");
00697 
00698         pjsip_endpt_respond_stateless( g_endpt, rdata, 
00699                                        500, &reason,
00700                                        NULL, NULL);
00701         return PJ_TRUE;
00702     } 
00703 
00704     /*
00705      * Generate Contact URI
00706      */
00707     if (pj_gethostip(AF, &hostaddr) != PJ_SUCCESS) {
00708         app_perror(THIS_FILE, "Unable to retrieve local host IP", status);
00709         return PJ_TRUE;
00710     }
00711     pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2);
00712 
00713     pj_ansi_sprintf(temp, "<sip:simpleuas@%s:%d>", 
00714                     hostip, SIP_PORT);
00715     local_uri = pj_str(temp);
00716 
00717     /*
00718      * Create UAS dialog.
00719      */
00720     status = pjsip_dlg_create_uas( pjsip_ua_instance(), 
00721                                    rdata,
00722                                    &local_uri, /* contact */
00723                                    &dlg);
00724     if (status != PJ_SUCCESS) {
00725         pjsip_endpt_respond_stateless(g_endpt, rdata, 500, NULL,
00726                                       NULL, NULL);
00727         return PJ_TRUE;
00728     }
00729 
00730     /* 
00731      * Get media capability from media endpoint: 
00732      */
00733 
00734     status = pjmedia_endpt_create_sdp( g_med_endpt, rdata->tp_info.pool,
00735                                        MAX_MEDIA_CNT, g_sock_info, &local_sdp);
00736     PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
00737 
00738 
00739     /* 
00740      * Create invite session, and pass both the UAS dialog and the SDP
00741      * capability to the session.
00742      */
00743     status = pjsip_inv_create_uas( dlg, rdata, local_sdp, 0, &g_inv);
00744     PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
00745 
00746 
00747     /*
00748      * Initially send 180 response.
00749      *
00750      * The very first response to an INVITE must be created with
00751      * pjsip_inv_initial_answer(). Subsequent responses to the same
00752      * transaction MUST use pjsip_inv_answer().
00753      */
00754     status = pjsip_inv_initial_answer(g_inv, rdata, 
00755                                       180, 
00756                                       NULL, NULL, &tdata);
00757     PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
00758 
00759 
00760     /* Send the 180 response. */  
00761     status = pjsip_inv_send_msg(g_inv, tdata); 
00762     PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
00763 
00764 
00765     /*
00766      * Now create 200 response.
00767      */
00768     status = pjsip_inv_answer( g_inv, 
00769                                200, NULL,       /* st_code and st_text */
00770                                NULL,            /* SDP already specified */
00771                                &tdata);
00772     PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
00773 
00774     /*
00775      * Send the 200 response.
00776      */
00777     status = pjsip_inv_send_msg(g_inv, tdata);
00778     PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
00779 
00780 
00781     /* Done. 
00782      * When the call is disconnected, it will be reported via the callback.
00783      */
00784 
00785     return PJ_TRUE;
00786 }
00787 
00788  
00789 
00790 /*
00791  * Callback when SDP negotiation has completed.
00792  * We are interested with this callback because we want to start media
00793  * as soon as SDP negotiation is completed.
00794  */
00795 static void call_on_media_update( pjsip_inv_session *inv,
00796                                   pj_status_t status)
00797 {
00798     pjmedia_stream_info stream_info;
00799     const pjmedia_sdp_session *local_sdp;
00800     const pjmedia_sdp_session *remote_sdp;
00801     pjmedia_port *media_port;
00802 
00803     if (status != PJ_SUCCESS) {
00804 
00805         app_perror(THIS_FILE, "SDP negotiation has failed", status);
00806 
00807         /* Here we should disconnect call if we're not in the middle 
00808          * of initializing an UAS dialog and if this is not a re-INVITE.
00809          */
00810         return;
00811     }
00812 
00813     /* Get local and remote SDP.
00814      * We need both SDPs to create a media session.
00815      */
00816     status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
00817 
00818     status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
00819 
00820 
00821     /* Create stream info based on the media audio SDP. */
00822     status = pjmedia_stream_info_from_sdp(&stream_info, inv->dlg->pool,
00823                                           g_med_endpt,
00824                                           local_sdp, remote_sdp, 0);
00825     if (status != PJ_SUCCESS) {
00826         app_perror(THIS_FILE,"Unable to create audio stream info",status);
00827         return;
00828     }
00829 
00830     /* If required, we can also change some settings in the stream info,
00831      * (such as jitter buffer settings, codec settings, etc) before we
00832      * create the stream.
00833      */
00834 
00835     /* Create new audio media stream, passing the stream info, and also the
00836      * media socket that we created earlier.
00837      */
00838     status = pjmedia_stream_create(g_med_endpt, inv->dlg->pool, &stream_info,
00839                                    g_med_transport[0], NULL, &g_med_stream);
00840     if (status != PJ_SUCCESS) {
00841         app_perror( THIS_FILE, "Unable to create audio stream", status);
00842         return;
00843     }
00844 
00845     /* Start the audio stream */
00846     status = pjmedia_stream_start(g_med_stream);
00847     if (status != PJ_SUCCESS) {
00848         app_perror( THIS_FILE, "Unable to start audio stream", status);
00849         return;
00850     }
00851 
00852     /* Get the media port interface of the audio stream. 
00853      * Media port interface is basicly a struct containing get_frame() and
00854      * put_frame() function. With this media port interface, we can attach
00855      * the port interface to conference bridge, or directly to a sound
00856      * player/recorder device.
00857      */
00858     pjmedia_stream_get_port(g_med_stream, &media_port);
00859 
00860     /* Create sound port */
00861     pjmedia_snd_port_create(inv->pool,
00862                             PJMEDIA_AUD_DEFAULT_CAPTURE_DEV,
00863                             PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV,
00864                             PJMEDIA_PIA_SRATE(&media_port->info),/* clock rate      */
00865                             PJMEDIA_PIA_CCNT(&media_port->info),/* channel count    */
00866                             PJMEDIA_PIA_SPF(&media_port->info), /* samples per frame*/
00867                             PJMEDIA_PIA_BITS(&media_port->info),/* bits per sample  */
00868                             0,
00869                             &g_snd_port);
00870 
00871     if (status != PJ_SUCCESS) {
00872         app_perror( THIS_FILE, "Unable to create sound port", status);
00873         PJ_LOG(3,(THIS_FILE, "%d %d %d %d",
00874                     PJMEDIA_PIA_SRATE(&media_port->info),/* clock rate      */
00875                     PJMEDIA_PIA_CCNT(&media_port->info),/* channel count    */
00876                     PJMEDIA_PIA_SPF(&media_port->info), /* samples per frame*/
00877                     PJMEDIA_PIA_BITS(&media_port->info) /* bits per sample  */
00878             ));
00879         return;
00880     }
00881 
00882     status = pjmedia_snd_port_connect(g_snd_port, media_port);
00883 
00884 
00885     /* Get the media port interface of the second stream in the session,
00886      * which is video stream. With this media port interface, we can attach
00887      * the port directly to a renderer/capture video device.
00888      */
00889 #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
00890     if (local_sdp->media_count > 1) {
00891         pjmedia_vid_stream_info vstream_info;
00892         pjmedia_vid_port_param vport_param;
00893 
00894         pjmedia_vid_port_param_default(&vport_param);
00895 
00896         /* Create stream info based on the media video SDP. */
00897         status = pjmedia_vid_stream_info_from_sdp(&vstream_info,
00898                                                   inv->dlg->pool, g_med_endpt,
00899                                                   local_sdp, remote_sdp, 1);
00900         if (status != PJ_SUCCESS) {
00901             app_perror(THIS_FILE,"Unable to create video stream info",status);
00902             return;
00903         }
00904 
00905         /* If required, we can also change some settings in the stream info,
00906          * (such as jitter buffer settings, codec settings, etc) before we
00907          * create the video stream.
00908          */
00909 
00910         /* Create new video media stream, passing the stream info, and also the
00911          * media socket that we created earlier.
00912          */
00913         status = pjmedia_vid_stream_create(g_med_endpt, NULL, &vstream_info,
00914                                            g_med_transport[1], NULL,
00915                                            &g_med_vstream);
00916         if (status != PJ_SUCCESS) {
00917             app_perror( THIS_FILE, "Unable to create video stream", status);
00918             return;
00919         }
00920 
00921         /* Start the video stream */
00922         status = pjmedia_vid_stream_start(g_med_vstream);
00923         if (status != PJ_SUCCESS) {
00924             app_perror( THIS_FILE, "Unable to start video stream", status);
00925             return;
00926         }
00927 
00928         if (vstream_info.dir & PJMEDIA_DIR_DECODING) {
00929             status = pjmedia_vid_dev_default_param(
00930                                 inv->pool, PJMEDIA_VID_DEFAULT_RENDER_DEV,
00931                                 &vport_param.vidparam);
00932             if (status != PJ_SUCCESS) {
00933                 app_perror(THIS_FILE, "Unable to get default param of video "
00934                            "renderer device", status);
00935                 return;
00936             }
00937 
00938             /* Get video stream port for decoding direction */
00939             pjmedia_vid_stream_get_port(g_med_vstream, PJMEDIA_DIR_DECODING,
00940                                         &media_port);
00941 
00942             /* Set format */
00943             pjmedia_format_copy(&vport_param.vidparam.fmt,
00944                                 &media_port->info.fmt);
00945             vport_param.vidparam.dir = PJMEDIA_DIR_RENDER;
00946             vport_param.active = PJ_TRUE;
00947 
00948             /* Create renderer */
00949             status = pjmedia_vid_port_create(inv->pool, &vport_param, 
00950                                              &g_vid_renderer);
00951             if (status != PJ_SUCCESS) {
00952                 app_perror(THIS_FILE, "Unable to create video renderer device",
00953                            status);
00954                 return;
00955             }
00956 
00957             /* Connect renderer to media_port */
00958             status = pjmedia_vid_port_connect(g_vid_renderer, media_port, 
00959                                               PJ_FALSE);
00960             if (status != PJ_SUCCESS) {
00961                 app_perror(THIS_FILE, "Unable to connect renderer to stream",
00962                            status);
00963                 return;
00964             }
00965         }
00966 
00967         /* Create capturer */
00968         if (vstream_info.dir & PJMEDIA_DIR_ENCODING) {
00969             status = pjmedia_vid_dev_default_param(
00970                                 inv->pool, PJMEDIA_VID_DEFAULT_CAPTURE_DEV,
00971                                 &vport_param.vidparam);
00972             if (status != PJ_SUCCESS) {
00973                 app_perror(THIS_FILE, "Unable to get default param of video "
00974                            "capture device", status);
00975                 return;
00976             }
00977 
00978             /* Get video stream port for decoding direction */
00979             pjmedia_vid_stream_get_port(g_med_vstream, PJMEDIA_DIR_ENCODING,
00980                                         &media_port);
00981 
00982             /* Get capturer format from stream info */
00983             pjmedia_format_copy(&vport_param.vidparam.fmt, 
00984                                 &media_port->info.fmt);
00985             vport_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
00986             vport_param.active = PJ_TRUE;
00987 
00988             /* Create capturer */
00989             status = pjmedia_vid_port_create(inv->pool, &vport_param, 
00990                                              &g_vid_capturer);
00991             if (status != PJ_SUCCESS) {
00992                 app_perror(THIS_FILE, "Unable to create video capture device",
00993                            status);
00994                 return;
00995             }
00996 
00997             /* Connect capturer to media_port */
00998             status = pjmedia_vid_port_connect(g_vid_capturer, media_port, 
00999                                               PJ_FALSE);
01000             if (status != PJ_SUCCESS) {
01001                 app_perror(THIS_FILE, "Unable to connect capturer to stream",
01002                            status);
01003                 return;
01004             }
01005         }
01006 
01007         /* Start streaming */
01008         if (g_vid_renderer) {
01009             status = pjmedia_vid_port_start(g_vid_renderer);
01010             if (status != PJ_SUCCESS) {
01011                 app_perror(THIS_FILE, "Unable to start video renderer",
01012                            status);
01013                 return;
01014             }
01015         }
01016         if (g_vid_capturer) {
01017             status = pjmedia_vid_port_start(g_vid_capturer);
01018             if (status != PJ_SUCCESS) {
01019                 app_perror(THIS_FILE, "Unable to start video capturer",
01020                            status);
01021                 return;
01022             }
01023         }
01024     }
01025 #endif  /* PJMEDIA_HAS_VIDEO */
01026 
01027     /* Done with media. */
01028 }
01029 
01030 

 


PJSIP Open Source, high performance, small footprint, and very very portable SIP stack
Copyright (C) 2006-2008 Teluu Inc.