Changes between Initial Version and Version 1 of Using_Standalone_ICE


Ignore:
Timestamp:
Mar 16, 2009 2:35:33 PM (13 years ago)
Author:
bennylp
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Using_Standalone_ICE

    v1 v1  
     1{{{ 
     2#!html 
     3<!-- MAIN TABLE START --> 
     4<table border=0 width="90%" align="center"><tr><td> 
     5}}} 
     6 
     7= Using Standalone PJNATH's ICE in (non-SIP) Applications = 
     8 
     9'''Table of Contents''' 
     10[[PageOutline(2-3,,inline)]] 
     11 
     12This article describes how to use the ICE stream transport of [http://www.pjsip.org/pjnath/docs/html/index.htm PJNATH] in a standalone, probably non-SIP/SDP applications. 
     13 
     14While reading this article, it's recommended to also open the [http://www.pjsip.org/pjnath/docs/html/group__PJNATH__ICE__STREAM__TRANSPORT.htm ICE stream transport reference] page for more detailed info for the API. This article will provide links to the API in that page for further reading about the detail specification of the API. 
     15 
     16[[BR]] 
     17 
     18== Introduction == #intro 
     19 
     20The [http://www.pjsip.org/pjnath/docs/html/index.htm PJNATH] (PJSIP NAT Traversal Helper) library contains various objects to assist application with NAT traversal, using standard based protocols such as STUN, TURN, and ICE. The "ultimate" object in the library is the [http://www.pjsip.org/pjnath/docs/html/group__PJNATH__ICE__STREAM__TRANSPORT.htm ICE stream transport] (will be called ''ice_strans'' for short in this article), where it wraps the STUN, TURN, and ICE functionality in one object and provides applications with API to send and receive data, as well as to perform ICE session management. 
     21 
     22From the library design point of view, all features in PJNATH are implemented in two layers, the transport independent/session layer, and the transport layer. The session layer contains only the logic to manage the corresponding session (for example, STUN, TURN, and ICE session). The transport layer wraps together the session object with socket(s) to make them ready to use transport objects. 
     23 
     24This article assumes that the application wants to use ICE transport, and not the ICE session (layer). 
     25 
     26[[BR]] 
     27== Terms == 
     28 
     29The following are terms used throughout this article: 
     30 
     31 '''ice_strans''' :: 
     32 The ICE stream transport as explained above. 
     33 
     34 '''ICE session''' :: 
     35 One multimedia session (e.g. one '''call''' session) between two ICE endpoint, within the ''ice_strans''. 
     36 
     37 '''ICE endpoint''' :: 
     38 The application which implements ICE. It is synonymous for ICE agent in the RFC. 
     39 
     40 
     41[[BR]] 
     42== Using standalone ICE in the application == #using 
     43 
     44=== Design === 
     45 
     46To use ICE, the application would need to replace it's send/receive socket(s) with [http://www.pjsip.org/pjnath/docs/html/group__PJNATH__ICE__STREAM__TRANSPORT.htm ICE stream transport] object (will be called ''ice_strans'' for short). Once the ICE session in ''ice_strans'' is up and running, application uses ''pj_ice_strans_sendto()'' to send data, and registers ''on_rx_data()'' in ''pj_ice_strans_cb'' structure to receive incoming data. 
     47 
     48For SIP/SDP usage, one ''ice_strans'' is good for one media stream, and one media stream may contain more than one media transports, or called component in ICE terms (e.g. one RTP and one RTCP). Each component typically will be provided with more than one candidates, e.g. local candidates, STUN candidate, and TURN candidate. It will be then ICE's job to work out which of the candidate pair to be used for the session. 
     49 
     50For non-SIP usage, it will be application's design decision whether to create one ''ice_strans'' with multiple components, or multiple ''ice_strans'' with one component each. Using former would definitely be simpler since we only need to work with one session, but the later would have the advantage of faster negotiation (by tens to hundreds of msecs) since the two ''ice_strans'' objects can then do the negotiation in parallel. 
     51 
     52 
     53[[BR]] 
     54=== Preparations === 
     55 
     56Before using PJNATH's ICE, several steps need to be done. 
     57 
     58The PJNATH library depends on the following libraries, hence they need to be built (or ported if necessary) and added to the application's linking specifications: 
     59 * [http://www.pjsip.org/pjlib/docs/html/main.htm PJLIB] (for memory access, timer, network I/O, as well as the basic data structure/framework) 
     60 * [http://www.pjsip.org/pjlib-util/docs/html/modules.htm PJLIB-UTIL] (mainly for the encryption algorithms needed by STUN) 
     61 
     62Several PJLIB objects need to be prepared by applications: 
     63 * at least one [http://www.pjsip.org/pjlib/docs/html/group__PJ__POOL__FACTORY.htm memory pool factory] instance is required for all PJLIB's based application. The memory pool factory is used to manage memory allocations by the libraries. 
     64 * at least one [http://www.pjsip.org/pjlib/docs/html/group__PJ__TIMER.htm timer heap] instance for managing the timers 
     65 * at least one [http://www.pjsip.org/pjlib/docs/html/group__PJ__IOQUEUE.htm ioqueue] instance for managing network I/O events. 
     66 
     67One object of each typically is enough, although application may create more to fine tune the performance (by limiting the number of objects that each manages) or for other reasons. 
     68 
     69Once these objects are created, there need to be something that polls the timer heap and the ioqueue (except on Symbian where polling is not used). Typically application would create at least one thread to do this polling. 
     70 
     71These are pretty ''basic'' tasks that are required for all PJLIB network based applications, so please see the samples for some code snippets (e.g. [http://trac.pjsip.org/repos/browser/pjproject/trunk/pjnath/src/pjturn-client/client_main.c#L106 turn-client sample]. 
     72 
     73[[BR]] 
     74=== Basic lifecycle === 
     75 
     76The following are the basic life cycle of ''ice_strans'': 
     77 * create ''ice_strans'' 
     78 * start one or more ICE session(s): 
     79     - create ICE session 
     80     - exchange ICE info with remote (username/password, candidate list). 
     81     - start ICE negotiation 
     82     - exchange data 
     83     - destroy ICE session 
     84     - repeat above to start new ICE session 
     85 * destroy ''ice_strans'' 
     86 
     87More will be explained below. 
     88 
     89 
     90[[BR]] 
     91=== Creating the ICE stream transport === 
     92 
     93To create the ''ice_strans'': 
     94 * initialize the [http://www.pjsip.org/pjnath/docs/html/structpj__ice__strans__cfg.htm pj_ice_strans_cfg]. Among other things, this structure contains settings required to enable and use STUN and TURN, as well as instances of the memory pool factory, timer heap and ioqueue (mentioned earlier) in the ''stun_cfg'' field. 
     95 * call [http://www.pjsip.org/pjnath/docs/html/group__PJNATH__ICE__STREAM__TRANSPORT.htm#gd62c480462e6b4267597e623a9a609c2 pj_ice_strans_create()] 
     96 * wait for the ''on_ice_complete()'' callback of the [http://www.pjsip.org/pjnath/docs/html/structpj__ice__strans__cb.htm pj_ice_strans_cb] to be called with ''op'' argument of '''PJ_ICE_STRANS_OP_INIT''', to indicate the status of the candidate gathering process (e.g. the result of STUN binding request and TURN allocation operations). The status of this candidate gathering process will be indicated in the ''status'' argument of the callback, with PJ_SUCCESS indicates succesful operation. 
     97 
     98 
     99Once ''ice_strans'' is created, it can be used to create ICE sessions. One ICE session represents one multimedia session between endpoints (i.e. one call session). After one session completes, the same ''ice_strans'' can be used to facilitate further sessions. Only one session may be active in one ''ice_strans'' at the same time. 
     100 
     101 
     102[[BR]] 
     103=== Working with session === 
     104 
     105The steps to use the session are typically as follows. 
     106 
     107==== Session creation ==== #sess_create 
     108 
     109Create the session by calling [http://www.pjsip.org/pjnath/docs/html/group__PJNATH__ICE__STREAM__TRANSPORT.htm#gbfa6ac93f3f56bab3ea8c357468f0826 pj_ice_strans_init_ice()], specifying the initial role of the (ICE) endpoint and optionally, the local username and password. 
     110 
     111 '''Note:''' :: 
     112 The role affects ICE's negotiation behavior, especially to determine which endpoint is the ''controlling'' side. While ICE provides ''role conflict'' resolution in its negotiation process, it's always recommended to supply this with correct initial value to avoid unnecessary round-trips for the ''role conflict'' resolution. 
     113 
     114 
     115==== Exchanging ICE information with remote endpoint ==== #sess_oa 
     116 
     117Before ICE negotiation can start, each ICE endpoint would need to know the ICE information of the other endpoint. On SIP/SDP usage, this will happen when the application exchanges SDP's between each other. For non-SIP usage, this will be up to exchange this information (as well as how to encode it). 
     118 
     119The following information needs to be sent to remote ICE endpoint: 
     120 * the local ICE session's username and password (the so called ''ufrag''/user fragment and password). 
     121 * the candidate list for each and all ICE components.  The [http://www.pjsip.org/pjnath/docs/html/group__PJNATH__ICE__STREAM__TRANSPORT.htm#g597a3c3493038d8b37ff0a63e8ad93e5 pj_ice_strans_enum_cands()] function is used to list the candidates of the specified ICE component. For each candidate, the following information needs to be exchanged: 
     122    - component ID 
     123    - candidate type (i.e. host, srflx, or relay) 
     124    - foundation ID 
     125    - priority 
     126    - transport type (only UDP is supported for now) 
     127    - transport address (address family, IP address, and port) 
     128    - optional related address (e.g. for srflx/STUN candidate, the related address is the local address where STUN request is sent from). This would only be used for troubleshooting purposes and is not required by ''ice_strans''. 
     129 * optionally the default candidate address for each ICE component. If remote doesn't support ICE, it can send data to this address. Application may also use this address to exchange data while ICE negotiation is in progress. The default candidate should be chosen from the candidate that is most likely to succeed, e.g. TURN, STUN, or one of the local candidate, in this order. Application may use [http://www.pjsip.org/pjnath/docs/html/group__PJNATH__ICE__STREAM__TRANSPORT.htm#gea33988e6b75062d5d23bc1280442b2d pj_ice_strans_get_def_cand()] function to get the default candidate from the ''ice_strans''. 
     130 
     131How to encode/decode as well as to exchange the above information in non-SIP usage is up to the application/usage scenario. In PJSIP sample usage where ICE is integrated with media transport, the task to encode/decode the above information is done by the PJMEDIA's ICE transport (pjmedia/transport_ice.[hc]), and the information will be exchanged in SDP offer/answer. Below is a sample SDP generated by PJSIP which contains ICE information, with the relevant ICE attributes in '''bold''': 
     132 
     133 v=0[[BR]] 
     134 o=- 3423381096 3423381096 IN IP4 81.178.x.y[[BR]] 
     135 s=pjmedia[[BR]] 
     136 c=IN IP4 '''81.178.x.y'''[[BR]] 
     137 t=0 0[[BR]] 
     138 a=X-nat:5 [[BR]] 
     139 m=audio '''4808''' RTP/AVP 103 102 104 117 3 0 8 9 101[[BR]] 
     140 '''a=rtcp:4809 IN IP4 81.178.x.y'''[[BR]] 
     141 a=rtpmap:103 speex/16000[[BR]] 
     142 a=rtpmap:102 speex/8000[[BR]] 
     143 a=rtpmap:104 speex/32000[[BR]] 
     144 a=rtpmap:117 iLBC/8000[[BR]] 
     145 a=fmtp:117 mode=30[[BR]] 
     146 a=sendrecv[[BR]] 
     147 a=rtpmap:101 telephone-event/8000[[BR]] 
     148 a=fmtp:101 0-15[[BR]] 
     149 '''a=ice-ufrag:2b2c6196''' [[BR]] 
     150 '''a=ice-pwd:06ea0fa8''' [[BR]] 
     151 '''a=candidate:Sc0a8000e 1 UDP 1694498815 81.178.x.y 4808 typ srflx raddr 192.168.0.14 rport 4808'''[[BR]] 
     152 '''a=candidate:Hc0a8000e 1 UDP 2130705151 192.168.0.14 4808 typ host'''[[BR]] 
     153 '''a=candidate:Sc0a8000e 2 UDP 1694498814 81.178.x.y 4809 typ srflx raddr 192.168.0.14 rport 4809'''[[BR]] 
     154 '''a=candidate:Hc0a8000e 2 UDP 2130705150 192.168.0.14 4809 typ host'''[[BR]] 
     155 
     156 
     157(Note: the c= and a=rtcp lines contain the default ICE candidate address for the RTP and RTCP components respectively. Public IP addresses have also been scrambled a bit in the SDP above to protect the innocence). 
     158 
     159The ''ice_strans'' would also need to '''receive''' the above information before it can start ICE negotiation. 
     160 
     161 
     162==== Starting ICE negotiation ==== #sess_start 
     163 
     164Once ICE endpoints have sent/received ICE information to/from remote, they can start ICE negotiation by calling [http://www.pjsip.org/pjnath/docs/html/group__PJNATH__ICE__STREAM__TRANSPORT.htm#g4a60bacfd840e40b5a94c7170c6f4530 pj_ice_strans_start_ice()]. This function would need the above ICE information as its arguments. Each endpoint will need to call this in order for the negotiation to succeed. 
     165 
     166ICE negotiation then will start. 
     167 
     168 
     169 '''Note:''' :: 
     170 * The timing when each endpoint starts ''pj_ice_strans_start_ice()'' doesn't have to be absolutely simultaneously, though the more synchronized the better of course to speed up negotiation, and there is also limit of approximately 7-8 seconds before ICE negotiation will complete with timeout status. 
     171 
     172==== Getting ICE negotiation result ==== #sess_negotiated 
     173 
     174Application will be notified about the result in the (again) ''on_ice_complete()'' callback of the [http://www.pjsip.org/pjnath/docs/html/structpj__ice__strans__cb.htm pj_ice_strans_cb], although this time with ''op'' argument of PJ_ICE_STRANS_OP_NEGOTIATION. The status of the operation will be indicated in the ''status'' argument of the callback, with PJ_SUCCESS indicates succesful negotiation. 
     175 
     176 '''Notes:''' :: 
     177 * It is possible that the number of components between the two ICE endpoints are different, e.g. we support RTCP but remote doesn't. The ''pj_ice_strans_get_running_comp_cnt()'' function can be used (after ICE negotiation completes) to find out how many components have been negotiated by ICE. Application can always deduce this information by comparing its local candidate list against remote's of course. 
     178 * See also the remarks about negotiation time in the global [#notes Notes] section at the end of this article. 
     179 
     180==== Sending and Receiving Data ==== #sess_data 
     181 
     182Use [http://www.pjsip.org/pjnath/docs/html/group__PJNATH__ICE__STREAM__TRANSPORT.htm#g5ce01f1ae17a6ac73afa98c3a3c619df pj_ice_strans_sendto()] to send data to remote ICE endpoint. Incoming data will be reported in ''on_rx_data()'' callback of the [http://www.pjsip.org/pjnath/docs/html/structpj__ice__strans__cb.htm pj_ice_strans_cb] structure. 
     183 
     184 
     185==== Finishing with the session ==== #sess_finish 
     186 
     187Once the session is done (e.g. call has ended), call [http://www.pjsip.org/pjnath/docs/html/group__PJNATH__ICE__STREAM__TRANSPORT.htm#gbda546ad9dbc4a53f406c85e157dfe73 pj_ice_strans_stop_ice()] to clean up local resources allocated for the session. 
     188 
     189Application may reuse this same ''ice_strans'' instance to start another session by repeating the steps from [#sess_create Session creation] above. 
     190 
     191 
     192[[BR]] 
     193=== Destroying ICE stream transport === 
     194 
     195Use [http://www.pjsip.org/pjnath/docs/html/group__PJNATH__ICE__STREAM__TRANSPORT.htm#g22326f9203e11399f710f46760d4ce8b pj_ice_strans_destroy()] to destroy the ICE stream transport itself. This will initiate TURN deallocation procedure (if TURN in used), and ultimately will close down sockets as well as all resources allocated by this ''ice_strans'' instance. 
     196 
     197Note that ''ice_strans'' destruction will not complete immediately if TURN is used (since it needs to wait for deallocation procedure), hence it is important that polling to the timer heap and ioqueue continues to be done. Application will not be notified when ''ice_strans'' destruction completes, it just needs to assume that the ''ice_strans'' object is no longer usable as soon as ''pj_ice_strans_destroy()'' is called. 
     198 
     199 
     200[[BR]] 
     201=== Notes === #notes 
     202 
     203Note that the information below applies to current PJSIP release (version 1.1 as of 2009/03/16). They may change (and definitely will be improved if we can) in subsequent releases. 
     204 
     205==== Keep-alive ==== 
     206 
     207Once the ''ice_strans'' is created, the STUN and TURN keep-alive will be done automatically and internally. The default STUN keep-alive period is 15 seconds (PJ_STUN_KEEP_ALIVE_SEC), and TURN is also 15 seconds (PJ_TURN_KEEP_ALIVE_SEC). 
     208 
     209==== IP address change ==== 
     210 
     211Changes in STUN mapped address is handled automatically by ''ice_strans'' via the STUN keep-alive exchanges, although currently there is no callback to notify application about this event. Call to [http://www.pjsip.org/pjnath/docs/html/group__PJNATH__ICE__STREAM__TRANSPORT.htm#g597a3c3493038d8b37ff0a63e8ad93e5 pj_ice_strans_enum_cands()] will get the updated address. 
     212 
     213Changes in local interface's IP address are not detected. 
     214 
     215If IP address change is of application's concern, currently we can only recommend the application to implement this detection, and destroy/recreate the ''ice_strans'' once it detects the IP address change. 
     216 
     217==== Negotiation time ==== 
     218 
     219ICE negotiation may take tens to hundreds of milliseconds to complete. The time it takes to complete negotiation depends on the number of candidates across all components in one single ''ice_strans'' and the round-trip between the two ICE endpoints. In our brief and non-scientific test, it took about 100-150 msec to complete, in scenario where two (SIP) endpoints were behind ADSL connection (both are in UK), with two components and 2-4 candidates per component. 
     220 
     221But please also note that '''it may take seconds''' for ICE to report negotiation failure. ICE will wait until all STUN retransmissions have timed-out, and with the default setting, it will take 7-8 seconds before it will report ICE negotiation failure. 
     222  
     223 
     224 
     225{{{ 
     226#!html 
     227<!-- MAIN TABLE END --> 
     228</td></tr></table> 
     229}}} 
     230