Index: pjproject/trunk/pjsip-apps/src/samples/icedemo.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/samples/icedemo.c	(revision 2599)
+++ pjproject/trunk/pjsip-apps/src/samples/icedemo.c	(revision 2600)
@@ -26,6 +26,14 @@
 #define THIS_FILE   "icedemo.c"
 
+/* For this demo app, configure longer STUN keep-alive time
+ * so that it does't clutter the screen output.
+ */
+#define KA_INTERVAL 300
+
+
+/* This is our global variables */
 static struct app_t
 {
+    /* Command line options are stored here */
     struct options
     {
@@ -41,4 +49,5 @@
     } opt;
 
+    /* Our global variables */
     pj_caching_pool	 cp;
     pj_pool_t		*pool;
@@ -48,4 +57,5 @@
     pj_ice_strans	*icest;
 
+    /* Variables to store parsed remote ICE info */
     struct rem_info
     {
@@ -60,4 +70,5 @@
 } icedemo;
 
+/* Utility to display error messages */
 static void icedemo_perror(const char *title, pj_status_t status)
 {
@@ -68,4 +79,7 @@
 }
 
+/* Utility: display error message and exit application (usually
+ * because of fatal error.
+ */
 static void err_exit(const char *title, pj_status_t status)
 {
@@ -103,6 +117,9 @@
 			}
 
-static pj_status_t icedemo_handle_events(unsigned max_msec,
-					 unsigned *p_count)
+/*
+ * This function checks for events from both timer and ioqueue (for
+ * network events). It is invoked by the worker thread.
+ */
+static pj_status_t handle_events(unsigned max_msec, unsigned *p_count)
 {
     enum { MAX_NET_EVENTS = 1 };
@@ -126,5 +143,7 @@
     if (timeout.msec >= 1000) timeout.msec = 999;
 
-    /* compare the value with the timeout to wait from timer, and use the minimum value. */
+    /* compare the value with the timeout to wait from timer, and use the 
+     * minimum value. 
+    */
     if (PJ_TIME_VAL_GT(timeout, max_timeout))
 	timeout = max_timeout;
@@ -165,4 +184,7 @@
 }
 
+/*
+ * This is the worker thread that polls event in the background.
+ */
 static int icedemo_worker_thread(void *unused)
 {
@@ -170,5 +192,5 @@
 
     while (!icedemo.thread_quit_flag) {
-	icedemo_handle_events(500, NULL);
+	handle_events(500, NULL);
     }
 
@@ -176,9 +198,15 @@
 }
 
-static void icedemo_on_rx_data(pj_ice_strans *ice_st,
-			       unsigned comp_id, 
-			       void *pkt, pj_size_t size,
-			       const pj_sockaddr_t *src_addr,
-			       unsigned src_addr_len)
+/*
+ * This is the callback that is registered to the ICE stream transport to
+ * receive notification about incoming data. By "data" it means application
+ * data such as RTP/RTCP, and not packets that belong to ICE signaling (such
+ * as STUN connectivity checks or TURN signaling).
+ */
+static void cb_on_rx_data(pj_ice_strans *ice_st,
+			  unsigned comp_id, 
+			  void *pkt, pj_size_t size,
+			  const pj_sockaddr_t *src_addr,
+			  unsigned src_addr_len)
 {
     char ipstr[PJ_INET6_ADDRSTRLEN+10];
@@ -188,12 +216,19 @@
     PJ_UNUSED_ARG(pkt);
 
-    PJ_LOG(3,(THIS_FILE, "Component %d: received %d bytes data from %s",
+    ((char*)pkt)[size] = '\0';
+
+    PJ_LOG(3,(THIS_FILE, "Component %d: received %d bytes data from %s: \"%s\"",
 	      comp_id, size,
-	      pj_sockaddr_print(src_addr, ipstr, sizeof(ipstr), 3)));
-}
-
-static void icedemo_on_ice_complete(pj_ice_strans *ice_st, 
-				    pj_ice_strans_op op,
-				    pj_status_t status)
+	      pj_sockaddr_print(src_addr, ipstr, sizeof(ipstr), 3),
+	      (char*)pkt));
+}
+
+/*
+ * This is the callback that is registered to the ICE stream transport to
+ * receive notification about ICE state progression.
+ */
+static void cb_on_ice_complete(pj_ice_strans *ice_st, 
+			       pj_ice_strans_op op,
+			       pj_status_t status)
 {
     const char *opname = 
@@ -213,4 +248,10 @@
 }
 
+
+/*
+ * This is the main application initialization function. It is called
+ * once (and only once) during application initialization sequence by 
+ * main().
+ */
 static pj_status_t icedemo_init(void)
 {
@@ -231,14 +272,21 @@
 
     /* Create application memory pool */
-    icedemo.pool = pj_pool_create(&icedemo.cp.factory, "icedemo", 512, 512, NULL);
+    icedemo.pool = pj_pool_create(&icedemo.cp.factory, "icedemo", 
+				  512, 512, NULL);
 
     /* Create timer heap for timer stuff */
-    CHECK( pj_timer_heap_create(icedemo.pool, 100, &icedemo.ice_cfg.stun_cfg.timer_heap) );
+    CHECK( pj_timer_heap_create(icedemo.pool, 100, 
+				&icedemo.ice_cfg.stun_cfg.timer_heap) );
 
     /* and create ioqueue for network I/O stuff */
-    CHECK( pj_ioqueue_create(icedemo.pool, 16, &icedemo.ice_cfg.stun_cfg.ioqueue) );
-
-    /* something must poll the timer heap and ioqueue, unless we're on Symbian */
-    CHECK( pj_thread_create(icedemo.pool, "icedemo", &icedemo_worker_thread, NULL, 0, 0, &icedemo.thread) );
+    CHECK( pj_ioqueue_create(icedemo.pool, 16, 
+			     &icedemo.ice_cfg.stun_cfg.ioqueue) );
+
+    /* something must poll the timer heap and ioqueue, 
+     * unless we're on Symbian where the timer heap and ioqueue run
+     * on themselves.
+     */
+    CHECK( pj_thread_create(icedemo.pool, "icedemo", &icedemo_worker_thread,
+			    NULL, 0, 0, &icedemo.thread) );
 
     icedemo.ice_cfg.af = pj_AF_INET();
@@ -249,5 +297,5 @@
 				      "resolver", 
 				      0, 
-				      icedemo.ice_cfg.stun_cfg.timer_heap, 
+				      icedemo.ice_cfg.stun_cfg.timer_heap,
 				      icedemo.ice_cfg.stun_cfg.ioqueue, 
 				      &icedemo.ice_cfg.resolver) );
@@ -276,4 +324,9 @@
 	    icedemo.ice_cfg.stun.port = PJ_STUN_PORT;
 	}
+
+	/* For this demo app, configure longer STUN keep-alive time
+	 * so that it does't clutter the screen output.
+	 */
+	icedemo.ice_cfg.stun.cfg.ka_interval = KA_INTERVAL;
     }
 
@@ -290,5 +343,5 @@
 	} else {
 	    icedemo.ice_cfg.turn.server = icedemo.opt.turn_srv;
-	    icedemo.ice_cfg.stun.port = PJ_STUN_PORT;
+	    icedemo.ice_cfg.turn.port = PJ_STUN_PORT;
 	}
 
@@ -305,7 +358,8 @@
 	    icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_UDP;
 
-	/* You may further customize TURN settings, e.g.:
-	    icedemo.ice_cfg.turn.alloc_param.lifetime = 300
+	/* For this demo app, configure longer keep-alive time
+	 * so that it does't clutter the screen output.
 	 */
+	icedemo.ice_cfg.turn.alloc_param.ka_interval = KA_INTERVAL;
     }
 
@@ -314,4 +368,8 @@
 }
 
+
+/*
+ * Create ICE stream transport instance, invoked from the menu.
+ */
 static void icedemo_create_instance(void)
 {
@@ -326,6 +384,6 @@
     /* init the callback */
     pj_bzero(&icecb, sizeof(icecb));
-    icecb.on_rx_data = icedemo_on_rx_data;
-    icecb.on_ice_complete = icedemo_on_ice_complete;
+    icecb.on_rx_data = cb_on_rx_data;
+    icecb.on_ice_complete = cb_on_ice_complete;
 
     /* create the instance */
@@ -339,6 +397,9 @@
     if (status != PJ_SUCCESS)
 	icedemo_perror("error creating ice", status);
-}
-
+    else
+	PJ_LOG(3,(THIS_FILE, "ICE instance successfully created"));
+}
+
+/* Utility to nullify parsed remote info */
 static void reset_rem_info(void)
 {
@@ -346,4 +407,8 @@
 }
 
+
+/*
+ * Destroy ICE stream transport instance, invoked from the menu.
+ */
 static void icedemo_destroy_instance(void)
 {
@@ -357,6 +422,12 @@
 
     reset_rem_info();
-}
-
+
+    PJ_LOG(3,(THIS_FILE, "ICE instance destroyed"));
+}
+
+
+/*
+ * Create ICE session, invoked from the menu.
+ */
 static void icedemo_init_session(unsigned rolechar)
 {
@@ -379,8 +450,14 @@
     if (status != PJ_SUCCESS)
 	icedemo_perror("error creating session", status);
+    else
+	PJ_LOG(3,(THIS_FILE, "ICE session created"));
 
     reset_rem_info();
 }
 
+
+/*
+ * Stop/destroy ICE session, invoked from the menu.
+ */
 static void icedemo_stop_session(void)
 {
@@ -400,8 +477,9 @@
     if (status != PJ_SUCCESS)
 	icedemo_perror("error stopping session", status);
+    else
+	PJ_LOG(3,(THIS_FILE, "ICE session stopped"));
 
     reset_rem_info();
 }
-
 
 #define PRINT(fmt, arg0, arg1, arg2, arg3, arg4, arg5)	    \
@@ -411,4 +489,6 @@
 	p += printed
 
+
+/* Utility to create a=candidate SDP attribute */
 static int print_cand(char buffer[], unsigned maxlen,
 		      const pj_ice_sess_cand *cand)
@@ -427,22 +507,7 @@
 	  (unsigned)pj_sockaddr_get_port(&cand->addr));
 
-    switch (cand->type) {
-    case PJ_ICE_CAND_TYPE_HOST:
-	PRINT("host\n", 0, 0, 0, 0, 0, 0);
-	break;
-    case PJ_ICE_CAND_TYPE_SRFLX:
-    case PJ_ICE_CAND_TYPE_RELAYED:
-    case PJ_ICE_CAND_TYPE_PRFLX:
-	PRINT("%s raddr %s rport %d\n",
-	      pj_ice_get_cand_type_name(cand->type),
-	      pj_sockaddr_print(&cand->rel_addr, ipaddr,
-				sizeof(ipaddr), 0),
-	      (int)pj_sockaddr_get_port(&cand->rel_addr),
-	      0, 0, 0);
-	break;
-    default:
-	pj_assert(!"Invalid candidate type");
-	return -PJ_EBUG;
-    }
+    PRINT("%s\n",
+	  pj_ice_get_cand_type_name(cand->type),
+	  0, 0, 0, 0, 0);
 
     if (p == buffer+maxlen)
@@ -454,5 +519,7 @@
 }
 
-/* Encode ICE information in SDP */
+/* 
+ * Encode ICE information in SDP.
+ */
 static int encode_session(char buffer[], unsigned maxlen)
 {
@@ -463,5 +530,6 @@
     pj_status_t status;
 
-    PRINT("v=0\no=- 3414953978 3414953978 IN IP4 127.0.0.1\ns=ice\nt=0 0\n", 
+    /* Write "dummy" SDP v=, o=, s=, and t= lines */
+    PRINT("v=0\no=- 3414953978 3414953978 IN IP4 localhost\ns=ice\nt=0 0\n", 
 	  0, 0, 0, 0, 0, 0);
 
@@ -470,4 +538,5 @@
 				NULL, NULL);
 
+    /* Write the a=ice-ufrag and a=ice-pwd attributes */
     PRINT("a=ice-ufrag:%.*s\na=ice-pwd:%.*s\n",
 	   (int)local_ufrag.slen,
@@ -477,4 +546,5 @@
 	   0, 0);
 
+    /* Write each component */
     for (comp=0; comp<icedemo.opt.comp_cnt; ++comp) {
 	unsigned j, cand_cnt;
@@ -482,9 +552,12 @@
 	char ipaddr[PJ_INET6_ADDRSTRLEN];
 
+	/* Get default candidate for the component */
 	status = pj_ice_strans_get_def_cand(icedemo.icest, comp+1, &cand[0]);
 	if (status != PJ_SUCCESS)
 	    return -status;
 
+	/* Write the default address */
 	if (comp==0) {
+	    /* For component 1, default address is in m= and c= lines */
 	    PRINT("m=audio %d RTP/AVP 0\n"
 		  "c=IN IP4 %s\n",
@@ -494,4 +567,5 @@
 		  0, 0, 0, 0);
 	} else if (comp==1) {
+	    /* For component 2, default address is in a=rtcp line */
 	    PRINT("a=rtcp:%d IN IP4 %s\n",
 		  (int)pj_sockaddr_get_port(&cand[0].addr),
@@ -500,4 +574,5 @@
 		  0, 0, 0, 0);
 	} else {
+	    /* For other components, we'll just invent this.. */
 	    PRINT("a=Xice-defcand:%d IN IP4 %s\n",
 		  (int)pj_sockaddr_get_port(&cand[0].addr),
@@ -507,4 +582,5 @@
 	}
 
+	/* Enumerate all candidates for this component */
 	status = pj_ice_strans_enum_cands(icedemo.icest, comp+1,
 					  &cand_cnt, cand);
@@ -512,4 +588,5 @@
 	    return -status;
 
+	/* And encode the candidates as SDP */
 	for (j=0; j<cand_cnt; ++j) {
 	    printed = print_cand(p, maxlen - (p-buffer), &cand[j]);
@@ -527,4 +604,9 @@
 }
 
+
+/*
+ * Show information contained in the ICE stream transport. This is
+ * invoked from the menu.
+ */
 static void icedemo_show_ice(void)
 {
@@ -593,7 +675,13 @@
 }
 
+
+/*
+ * Input and parse SDP from the remote (containing remote's ICE information) 
+ * and save it to global variables.
+ */
 static void icedemo_input_remote(void)
 {
     char linebuf[80];
+    unsigned media_cnt = 0;
     unsigned comp0_port = 0;
     char     comp0_addr[80];
@@ -626,4 +714,8 @@
 	if (len==0)
 	    break;
+
+	/* Ignore subsequent media descriptors */
+	if (media_cnt > 1)
+	    continue;
 
 	switch (line[0]) {
@@ -633,4 +725,10 @@
 		char media[32], portstr[32];
 
+		++media_cnt;
+		if (media_cnt > 1) {
+		    puts("Media line ignored");
+		    break;
+		}
+
 		cnt = sscanf(line+2, "%s %s RTP/", media, portstr);
 		if (cnt != 2) {
@@ -640,4 +738,5 @@
 
 		comp0_port = atoi(portstr);
+		
 	    }
 	    break;
@@ -792,5 +891,6 @@
     }
 
-    printf("Done, %d remote candidates added\n", icedemo.rem.cand_cnt);
+    PJ_LOG(3, (THIS_FILE, "Done, %d remote candidate(s) added", 
+	       icedemo.rem.cand_cnt));
     return;
 
@@ -799,4 +899,8 @@
 }
 
+
+/*
+ * Start ICE negotiation! This function is invoked from the menu.
+ */
 static void icedemo_start_nego(void)
 {
@@ -818,4 +922,6 @@
 	return;
     }
+
+    PJ_LOG(3,(THIS_FILE, "Starting ICE negotiation.."));
 
     status = pj_ice_strans_start_ice(icedemo.icest, 
@@ -830,4 +936,8 @@
 }
 
+
+/*
+ * Send application data to remote agent.
+ */
 static void icedemo_send_data(unsigned comp_id, const char *data)
 {
@@ -844,8 +954,10 @@
     }
 
+    /*
     if (!pj_ice_strans_sess_is_complete(icedemo.icest)) {
 	PJ_LOG(1,(THIS_FILE, "Error: ICE negotiation has not been started or is in progress"));
 	return;
     }
+    */
 
     if (comp_id > pj_ice_strans_get_running_comp_cnt(icedemo.icest)) {
@@ -863,9 +975,43 @@
 }
 
+
+/*
+ * Display help for the menu.
+ */
 static void icedemo_help_menu(void)
 {
-}
-
-
+    puts("");
+    puts("-= Help on using ICE and this icedemo program =-");
+    puts("");
+    puts("This application demonstrates how to use ICE in pjnath without having\n"
+	 "to use the SIP protocol. To use this application, you will need to run\n"
+	 "two instances of this application, to simulate two ICE agents.\n");
+
+    puts("Basic ICE flow:\n"
+	 " create instance [menu \"c\"]\n"
+	 " repeat these steps as wanted:\n"
+	 "   - init session as offerer or answerer [menu \"i\"]\n"
+	 "   - display our SDP [menu \"s\"]\n"
+	 "   - \"send\" our SDP from the \"show\" output above to remote, by\n"
+	 "     copy-pasting the SDP to the other icedemo application\n"
+	 "   - parse remote SDP, by pasting SDP generated by the other icedemo\n"
+	 "     instance [menu \"r\"]\n"
+	 "   - begin ICE negotiation in our end [menu \"b\"]\n"
+	 "   - begin ICE negotiation in the other icedemo instance\n"
+	 "   - ICE negotiation will run, and result will be printed to screen\n"
+	 "   - send application data to remote [menu \"x\"]\n"
+	 "   - end/stop ICE session [menu \"e\"]\n"
+	 " destroy instance [menu \"d\"]\n"
+	 "");
+
+    puts("");
+    puts("This concludes the help screen.");
+    puts("");
+}
+
+
+/*
+ * Display console menu
+ */
 static void icedemo_print_menu(void)
 {
@@ -889,4 +1035,7 @@
 
 
+/*
+ * Main console loop.
+ */
 static void icedemo_console(void)
 {
@@ -916,8 +1065,13 @@
 
 	if (strcmp(cmd, "create")==0 || strcmp(cmd, "c")==0) {
+
 	    icedemo_create_instance();
+
 	} else if (strcmp(cmd, "destroy")==0 || strcmp(cmd, "d")==0) {
+
 	    icedemo_destroy_instance();
+
 	} else if (strcmp(cmd, "init")==0 || strcmp(cmd, "i")==0) {
+
 	    char *role = strtok(NULL, SEP);
 	    if (role)
@@ -925,27 +1079,46 @@
 	    else
 		puts("error: Role required");
+
 	} else if (strcmp(cmd, "stop")==0 || strcmp(cmd, "e")==0) {
+
 	    icedemo_stop_session();
+
 	} else if (strcmp(cmd, "show")==0 || strcmp(cmd, "s")==0) {
+
 	    icedemo_show_ice();
+
 	} else if (strcmp(cmd, "remote")==0 || strcmp(cmd, "r")==0) {
+
 	    icedemo_input_remote();
+
 	} else if (strcmp(cmd, "start")==0 || strcmp(cmd, "b")==0) {
+
 	    icedemo_start_nego();
+
 	} else if (strcmp(cmd, "send")==0 || strcmp(cmd, "x")==0) {
-	    char *strcomp = strtok(NULL, SEP);
-
-	    if (!strcmp) {
+
+	    char *comp = strtok(NULL, SEP);
+
+	    if (!comp) {
 		PJ_LOG(1,(THIS_FILE, "Error: component ID required"));
 	    } else {
-		char *data = strcomp + strlen(strcomp) + 1;
-		icedemo_send_data(atoi(strcomp), data);
+		char *data = comp + strlen(comp) + 1;
+		if (!data)
+		    data = "";
+		icedemo_send_data(atoi(comp), data);
 	    }
+
 	} else if (strcmp(cmd, "help")==0 || strcmp(cmd, "h")==0) {
+
 	    icedemo_help_menu();
+
 	} else if (strcmp(cmd, "quit")==0 || strcmp(cmd, "q")==0) {
+
 	    app_quit = PJ_TRUE;
+
 	} else {
+
 	    printf("Invalid command '%s'\n", cmd);
+
 	}
     }
@@ -953,4 +1126,7 @@
 
 
+/*
+ * Display program usage.
+ */
 static void icedemo_usage()
 {
@@ -982,4 +1158,7 @@
 
 
+/*
+ * And here's the main()
+ */
 int main(int argc, char *argv[])
 {
