Index: /pjproject/trunk/pjsip-apps/src/samples/siprtp.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/siprtp.c	(revision 539)
+++ /pjproject/trunk/pjsip-apps/src/samples/siprtp.c	(revision 540)
@@ -97,9 +97,14 @@
 
 
-/* A bidirectional media stream */
+/* A bidirectional media stream created when the call is active. */
 struct media_stream
 {
     /* Static: */
-    pj_uint16_t		 port;		    /* RTP port (RTCP is +1)	*/
+    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: */
@@ -111,8 +116,4 @@
     unsigned		 bytes_per_frame;   /* frame size.		*/
 
-    /* Sockets: */
-    pj_sock_t		 rtp_sock;	    /* RTP socket.		*/
-    pj_sock_t		 rtcp_sock;	    /* RTCP socket.		*/
-
     /* RTP session: */
     pjmedia_rtp_session	 out_sess;	    /* outgoing RTP session	*/
@@ -123,9 +124,12 @@
 
     /* Thread: */
-    pj_bool_t		 thread_quit_flag;  /* worker thread quit flag	*/
-    pj_thread_t		*thread;	    /* RTP/RTCP worker 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
 {
@@ -133,5 +137,5 @@
     pjsip_inv_session	*inv;
     unsigned		 media_count;
-    struct media_stream	 media[2];
+    struct media_stream	 media[1];
     pj_time_val		 start_time;
     pj_time_val		 response_time;
@@ -142,4 +146,5 @@
 
 
+/* Application's global variables */
 static struct app
 {
@@ -151,5 +156,5 @@
     int			 sip_port;
     int			 rtp_start_port;
-    char		*local_addr;
+    pj_str_t		 local_addr;
     pj_str_t		 local_uri;
     pj_str_t		 local_contact;
@@ -169,5 +174,5 @@
     pjsip_endpoint	*sip_endpt;
     pj_bool_t		 thread_quit;
-    pj_thread_t		*thread[1];
+    pj_thread_t		*sip_thread[1];
 
     pjmedia_endpt	*med_endpt;
@@ -196,5 +201,5 @@
 
 /* Worker thread prototype */
-static int worker_thread(void *arg);
+static int sip_worker_thread(void *arg);
 
 /* Create SDP for call */
@@ -208,4 +213,13 @@
 /* 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 */
@@ -247,5 +261,5 @@
     { 4,  "G723", 8000, 6400,  30, "G.723.1" },
     { 8,  "PCMA", 8000, 64000, 20, "G.711 ALaw" },
-    { 18, "G729", 8000, 8000, 20, "G.729" },
+    { 18, "G729", 8000, 8000,  20, "G.729" },
 };
 
@@ -256,4 +270,5 @@
 static pj_status_t init_sip()
 {
+    unsigned i;
     pj_status_t status;
 
@@ -268,23 +283,8 @@
     app.pool = pj_pool_create(&app.cp.factory, "app", 1000, 1000, NULL);
 
-    /* Create global endpoint: */
-    {
-	const pj_str_t *hostname;
-	const char *endpt_name;
-
-	/* Endpoint MUST be assigned a globally unique name.
-	 * The name will be used as the hostname in Warning header.
-	 */
-
-	/* For this implementation, we'll use hostname for simplicity */
-	hostname = pj_gethostname();
-	endpt_name = hostname->ptr;
-
-	/* Create the endpoint: */
-
-	status = pjsip_endpt_create(&app.cp.factory, endpt_name, 
-				    &app.sip_endpt);
-	PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-    }
+    /* Create the endpoint: */
+    status = pjsip_endpt_create(&app.cp.factory, pj_gethostname()->ptr, 
+				&app.sip_endpt);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
 
@@ -299,11 +299,11 @@
 	addr.sin_port = pj_htons((pj_uint16_t)app.sip_port);
 
-	if (app.local_addr) {
-	    addrname.host = pj_str(app.local_addr);
+	if (app.local_addr.slen) {
+	    addrname.host = app.local_addr;
 	    addrname.port = app.sip_port;
 	}
 
 	status = pjsip_udp_transport_start( app.sip_endpt, &addr, 
-					    (app.local_addr ? &addrname:NULL), 
+					    (app.local_addr.slen ? &addrname:NULL),
 					    1, NULL);
 	if (status != PJ_SUCCESS) {
@@ -343,4 +343,7 @@
     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
+    /* Init calls */
+    for (i=0; i<app.max_calls; ++i)
+	app.call[i].index = i;
 
     /* Done */
@@ -358,8 +361,8 @@
     app.thread_quit = 1;
     for (i=0; i<app.thread_count; ++i) {
-	if (app.thread[i]) {
-	    pj_thread_join(app.thread[i]);
-	    pj_thread_destroy(app.thread[i]);
-	    app.thread[i] = NULL;
+	if (app.sip_thread[i]) {
+	    pj_thread_join(app.sip_thread[i]);
+	    pj_thread_destroy(app.sip_thread[i]);
+	    app.sip_thread[i] = NULL;
 	}
     }
@@ -377,14 +380,7 @@
 static pj_status_t init_media()
 {
-    pj_ioqueue_t *ioqueue;
     unsigned	i, count;
     pj_uint16_t	rtp_port;
-    pj_str_t	temp;
-    pj_sockaddr_in  addr;
     pj_status_t	status;
-
-
-    /* Get the ioqueue from the SIP endpoint */
-    ioqueue = pjsip_endpt_get_ioqueue(app.sip_endpt);
 
 
@@ -392,77 +388,47 @@
      * initialized.
      */
-    status = pjmedia_endpt_create(&app.cp.factory, ioqueue, 1, 
-				  &app.med_endpt);
+    status = pjmedia_endpt_create(&app.cp.factory, NULL, 1, &app.med_endpt);
     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
 
-    /* Add G711 codec */
+    /* Must register codecs to be supported */
     pjmedia_codec_g711_init(app.med_endpt);
-
-    /* Determine address to bind socket */
-    pj_memset(&addr, 0, sizeof(addr));
-    addr.sin_family = PJ_AF_INET;
-    i = pj_inet_aton(pj_cstr(&temp, app.local_addr), &addr.sin_addr);
-    if (i == 0) {
-	PJ_LOG(3,(THIS_FILE, 
-		  "Error: invalid local address %s (expecting IP)",
-		  app.local_addr));
-	return -1;
-    }
 
     /* RTP port counter */
     rtp_port = (pj_uint16_t)(app.rtp_start_port & 0xFFFE);
 
-
-    /* Init media sockets. */
+    /* Init media transport for all calls. */
     for (i=0, count=0; i<app.max_calls; ++i, ++count) {
 
-	int retry;
-
-	app.call[i].index = i;
-
-	/* Repeat binding media socket to next port when fails to bind
-	 * to current port number.
-	 */
-	retry = 0;
-	status = -1;
-	for (retry=0; status!=PJ_SUCCESS && retry<100; ++retry,rtp_port+=2) {
-	    struct media_stream *m = &app.call[i].media[0];
-
-	    m->port = rtp_port;
-
-	    /* Create and bind RTP socket */
-	    status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0,
-				    &m->rtp_sock);
-	    if (status != PJ_SUCCESS)
-		goto on_error;
-
-	    addr.sin_port = pj_htons(rtp_port);
-	    status = pj_sock_bind(m->rtp_sock, &addr, sizeof(addr));
-	    if (status != PJ_SUCCESS) {
-		pj_sock_close(m->rtp_sock), m->rtp_sock=0;
-		continue;
+	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;
+		}
 	    }
-
-
-	    /* Create and bind RTCP socket */
-	    status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0,
-				    &m->rtcp_sock);
-	    if (status != PJ_SUCCESS)
-		goto on_error;
-
-	    addr.sin_port = pj_htons((pj_uint16_t)(rtp_port+1));
-	    status = pj_sock_bind(m->rtcp_sock, &addr, sizeof(addr));
-	    if (status != PJ_SUCCESS) {
-		pj_sock_close(m->rtp_sock), m->rtp_sock=0;
-		pj_sock_close(m->rtcp_sock), m->rtcp_sock=0;
-		continue;
-	    }
-
 	}
 
 	if (status != PJ_SUCCESS)
 	    goto on_error;
-
     }
 
@@ -471,11 +437,5 @@
 
 on_error:
-    for (i=0; i<count; ++i) {
-	struct media_stream *m = &app.call[i].media[0];
-
-	pj_sock_close(m->rtp_sock), m->rtp_sock=0;
-	pj_sock_close(m->rtcp_sock), m->rtcp_sock=0;
-    }
-
+    destroy_media();
     return status;
 }
@@ -490,11 +450,13 @@
 
     for (i=0; i<app.max_calls; ++i) {
-	struct media_stream *m = &app.call[i].media[0];
-
-	if (m->rtp_sock)
-	    pj_sock_close(m->rtp_sock), m->rtp_sock = 0;
-
-	if (m->rtcp_sock)
-	    pj_sock_close(m->rtcp_sock), m->rtcp_sock = 0;
+	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;
+	    }
+	}
     }
 
@@ -795,6 +757,6 @@
 
 
-/* Worker thread */
-static int worker_thread(void *arg)
+/* Worker thread for SIP */
+static int sip_worker_thread(void *arg)
 {
     PJ_UNUSED_ARG(arg);
@@ -864,5 +826,5 @@
     app.sip_port = 5060;
     app.rtp_start_port = RTP_START_PORT;
-    app.local_addr = ip_addr;
+    app.local_addr = pj_str(ip_addr);
     app.log_level = 5;
     app.app_log_level = 3;
@@ -899,5 +861,5 @@
 	    break;
 	case 'i':
-	    app.local_addr = pj_optarg;
+	    app.local_addr = pj_str(pj_optarg);
 	    break;
 
@@ -942,5 +904,5 @@
 
     /* Build local URI and contact */
-    pj_ansi_sprintf( local_uri, "sip:%s:%d", app.local_addr, app.sip_port);
+    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;
@@ -966,8 +928,12 @@
     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 */
@@ -988,5 +954,5 @@
     sdp->conn->net_type = pj_str("IN");
     sdp->conn->addr_type = pj_str("IP4");
-    sdp->conn->addr = pj_str(app.local_addr);
+    sdp->conn->addr = app.local_addr;
 
 
@@ -1003,5 +969,5 @@
     /* Standard media info: */
     m->desc.media = pj_str("audio");
-    m->desc.port = audio->port;
+    m->desc.port = pj_ntohs(tpinfo.skinfo.rtp_addr_name.sin_port);
     m->desc.port_count = 1;
     m->desc.transport = pj_str("RTP/AVP");
@@ -1068,4 +1034,72 @@
 #endif
 
+
+/*
+ * 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)
+{
+    struct media_stream *strm;
+    pj_status_t status;
+    const pjmedia_rtp_hdr *hdr;
+    const void *payload;
+    unsigned payload_len;
+
+    strm = user_data;
+
+    /* Discard packet if media is inactive */
+    if (!strm->active)
+	return;
+
+    /* Check for errors */
+    if (size < 0) {
+	app_perror(THIS_FILE, "RTP recv() error", -size);
+	return;
+    }
+
+    /* Decode RTP packet. */
+    status = pjmedia_rtp_decode_rtp(&strm->in_sess, 
+				    pkt, size, 
+				    &hdr, &payload, &payload_len);
+    if (status != PJ_SUCCESS) {
+	app_perror(THIS_FILE, "RTP decode error", status);
+	return;
+    }
+
+    //PJ_LOG(4,(THIS_FILE, "Rx seq=%d", pj_ntohs(hdr->seq)));
+
+    /* Update the RTCP session. */
+    pjmedia_rtcp_rx_rtp(&strm->rtcp, pj_ntohs(hdr->seq),
+			pj_ntohl(hdr->ts), payload_len);
+
+    /* Update RTP session */
+    pjmedia_rtp_session_update(&strm->in_sess, hdr, NULL);
+
+}
+
+/*
+ * 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)
+{
+    struct media_stream *strm;
+
+    strm = user_data;
+
+    /* Discard packet if media is inactive */
+    if (!strm->active)
+	return;
+
+    /* Check for errors */
+    if (size < 0) {
+	app_perror(THIS_FILE, "Error receiving RTCP packet", -size);
+	return;
+    }
+
+    /* Update RTCP session */
+    pjmedia_rtcp_rx_rtcp(&strm->rtcp, pkt, size);
+}
+
+
 /* 
  * Media thread 
@@ -1085,4 +1119,6 @@
     boost_priority();
 
+    /* Let things settle */
+    pj_thread_sleep(1000);
 
     msec_interval = strm->samples_per_frame * 1000 / strm->clock_rate;
@@ -1097,14 +1133,18 @@
 
     while (!strm->thread_quit_flag) {
-	pj_fd_set_t set;
 	pj_timestamp now, lesser;
 	pj_time_val timeout;
-	int rc;
+	pj_bool_t send_rtp, send_rtcp;
+
+	send_rtp = send_rtcp = PJ_FALSE;
 
 	/* Determine how long to sleep */
-	if (next_rtp.u64 < next_rtcp.u64)
+	if (next_rtp.u64 < next_rtcp.u64) {
 	    lesser = next_rtp;
-	else
+	    send_rtp = PJ_TRUE;
+	} else {
 	    lesser = next_rtcp;
+	    send_rtcp = PJ_TRUE;
+	}
 
 	pj_get_timestamp(&now);
@@ -1122,74 +1162,14 @@
 	}
 
-	PJ_FD_ZERO(&set);
-	PJ_FD_SET(strm->rtp_sock, &set);
-	PJ_FD_SET(strm->rtcp_sock, &set);
-
-	rc = pj_sock_select(FD_SETSIZE, &set, NULL, NULL, &timeout);
-
-	if (rc < 0) {
-	    pj_thread_sleep(10);
-	    continue;
-	}
-
-	if (rc > 0 && PJ_FD_ISSET(strm->rtp_sock, &set)) {
-
-	    /*
-	     * Process incoming RTP packet.
-	     */
-	    pj_status_t status;
-	    pj_ssize_t size;
-	    const pjmedia_rtp_hdr *hdr;
-	    const void *payload;
-	    unsigned payload_len;
-
-	    size = sizeof(packet);
-	    status = pj_sock_recv(strm->rtp_sock, packet, &size, 0);
-	    if (status != PJ_SUCCESS) {
-		app_perror(THIS_FILE, "RTP recv() error", status);
-		pj_thread_sleep(10);
-		continue;
-	    }
-
-
-	    /* Decode RTP packet. */
-	    status = pjmedia_rtp_decode_rtp(&strm->in_sess, 
-					    packet, size, 
-					    &hdr, 
-					    &payload, &payload_len);
-	    if (status != PJ_SUCCESS) {
-		app_perror(THIS_FILE, "RTP decode error", status);
-		continue;
-	    }
-
-	    /* Update the RTCP session. */
-	    pjmedia_rtcp_rx_rtp(&strm->rtcp, pj_ntohs(hdr->seq),
-				pj_ntohl(hdr->ts), payload_len);
-
-	    /* Update RTP session */
-	    pjmedia_rtp_session_update(&strm->in_sess, hdr, NULL);
-	} 
-	
-	if (rc > 0 && PJ_FD_ISSET(strm->rtcp_sock, &set)) {
-
-	    /*
-	     * Process incoming RTCP
-	     */
-	    pj_status_t status;
-	    pj_ssize_t size;
-
-	    size = sizeof(packet);
-	    status = pj_sock_recv( strm->rtcp_sock, packet, &size, 0);
-	    if (status != PJ_SUCCESS) {
-		app_perror(THIS_FILE, "Error receiving RTCP packet", status);
-		pj_thread_sleep(10);
-	    } else
-		pjmedia_rtcp_rx_rtcp(&strm->rtcp, packet, size);
-	}
-
+	/* Wait for next interval */
+	//if (timeout.sec!=0 && timeout.msec!=0) {
+	    pj_thread_sleep(PJ_TIME_VAL_MSEC(timeout));
+	    if (strm->thread_quit_flag)
+		break;
+	//}
 
 	pj_get_timestamp(&now);
 
-	if (next_rtp.u64 <= now.u64) {
+	if (send_rtp || next_rtp.u64 <= now.u64) {
 	    /*
 	     * Time to send RTP packet.
@@ -1208,4 +1188,6 @@
 	    if (status == PJ_SUCCESS) {
 
+		//PJ_LOG(4,(THIS_FILE, "\t\tTx seq=%d", pj_ntohs(hdr->seq)));
+
 		/* Copy RTP header to packet */
 		pj_memcpy(packet, hdr, hdrlen);
@@ -1216,11 +1198,11 @@
 		/* Send RTP packet */
 		size = hdrlen + strm->bytes_per_frame;
-		status = pj_sock_sendto( strm->rtp_sock, packet, &size, 0,
-					 &strm->si.rem_addr, 
-					 sizeof(strm->si.rem_addr));
-
+		status = pjmedia_transport_send_rtp(strm->transport, 
+						    packet, size);
 		if (status != PJ_SUCCESS)
 		    app_perror(THIS_FILE, "Error sending RTP packet", status);
 
+	    } else {
+		pj_assert(!"RTP encode() error");
 	    }
 
@@ -1233,5 +1215,5 @@
 
 
-	if (next_rtcp.u64 <= now.u64) {
+	if (send_rtcp || next_rtcp.u64 <= now.u64) {
 	    /*
 	     * Time to send RTCP packet.
@@ -1239,7 +1221,5 @@
 	    pjmedia_rtcp_pkt *rtcp_pkt;
 	    int rtcp_len;
-	    pj_sockaddr_in rem_addr;
 	    pj_ssize_t size;
-	    int port;
 	    pj_status_t status;
 
@@ -1248,13 +1228,8 @@
 
     
-	    /* Calculate address based on RTP address */
-	    rem_addr = strm->si.rem_addr;
-	    port = pj_ntohs(strm->si.rem_addr.sin_port) + 1;
-	    rem_addr.sin_port = pj_htons((pj_uint16_t)port);
-
 	    /* Send packet */
 	    size = rtcp_len;
-	    status = pj_sock_sendto(strm->rtcp_sock, rtcp_pkt, &size, 0,
-				    &rem_addr, sizeof(rem_addr));
+	    status = pjmedia_transport_send_rtcp(strm->transport,
+						 rtcp_pkt, size);
 	    if (status != PJ_SUCCESS) {
 		app_perror(THIS_FILE, "Error sending RTCP packet", status);
@@ -1339,4 +1314,14 @@
 
 
+    /* Attach media to transport */
+    status = pjmedia_transport_attach(audio->transport, audio, 
+				      &audio->si.rem_addr, 
+				      sizeof(pj_sockaddr_in),
+				      &on_rx_rtp,
+				      &on_rx_rtcp);
+    if (status != PJ_SUCCESS) {
+	app_perror(THIS_FILE, "Error on pjmedia_transport_attach()", status);
+	return;
+    }
 
     /* Start media thread. */
@@ -1346,5 +1331,9 @@
     if (status != PJ_SUCCESS) {
 	app_perror(THIS_FILE, "Error creating media thread", status);
-    }
+	return;
+    }
+
+    /* Set the media as active */
+    audio->active = PJ_TRUE;
 }
 
@@ -1357,4 +1346,7 @@
 
     if (audio->thread) {
+
+	audio->active = PJ_FALSE;
+
 	audio->thread_quit_flag = 1;
 	pj_thread_join(audio->thread);
@@ -1363,31 +1355,5 @@
 	audio->thread_quit_flag = 0;
 
-	/* Flush RTP/RTCP packets */
-	{
-	    pj_fd_set_t set;
-	    pj_time_val timeout = {0, 0};
-	    char packet[1500];
-	    pj_ssize_t size;
-	    pj_status_t status;
-	    int rc;
-
-	    do {
-		PJ_FD_ZERO(&set);
-		PJ_FD_SET(audio->rtp_sock, &set);
-		PJ_FD_SET(audio->rtcp_sock, &set);
-
-		rc = pj_sock_select(FD_SETSIZE, &set, NULL, NULL, &timeout);
-		if (rc > 0 && PJ_FD_ISSET(audio->rtp_sock, &set)) {
-		    size = sizeof(packet);
-		    status = pj_sock_recv(audio->rtp_sock, packet, &size, 0);
-
-		} 
-		if (rc > 0 && PJ_FD_ISSET(audio->rtcp_sock, &set)) {
-		    size = sizeof(packet);
-		    status = pj_sock_recv(audio->rtcp_sock, packet, &size, 0);
-		}
-
-	    } while (rc > 0);
-	}
+	pjmedia_transport_detach(audio->transport, audio);
     }
 }
@@ -1431,4 +1397,10 @@
 	    continue;
 	hangup_call(i);
+    }
+    
+    /* Wait until all calls are terminated */
+    for (i=0; i<app.max_calls; ++i) {
+	while (app.call[i].inv)
+	    pj_thread_sleep(10);
     }
 }
@@ -1682,6 +1654,6 @@
     /* Start worker threads */
     for (i=0; i<app.thread_count; ++i) {
-	pj_thread_create( app.pool, "app", &worker_thread, NULL,
-			  0, 0, &app.thread[i]);
+	pj_thread_create( app.pool, "app", &sip_worker_thread, NULL,
+			  0, 0, &app.sip_thread[i]);
     }
 
