PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
					    const pjsua_transport_config *cfg,
					    pjsua_transport_id *p_id)
{
    pjsip_transport *tp;
    unsigned id;
    pj_status_t status;

    PJSUA_LOCK();

    /* Find empty transport slot */
    for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
	if (pjsua_var.tpdata[id].data.ptr == NULL)
	    break;
    }

    if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
	status = PJ_ETOOMANY;
	pjsua_perror(THIS_FILE, "Error creating transport", status);
	goto on_return;
    }

    /* Create the transport */
    if (type==PJSIP_TRANSPORT_UDP || type==PJSIP_TRANSPORT_UDP6) {
	/*
	 * Create UDP transport (IPv4 or IPv6).
	 */
	pjsua_transport_config config;
	char hostbuf[PJ_INET6_ADDRSTRLEN];
	pj_sock_t sock = PJ_INVALID_SOCKET;
	pj_sockaddr pub_addr;
	pjsip_host_port addr_name;

	/* Supply default config if it's not specified */
	if (cfg == NULL) {
	    pjsua_transport_config_default(&config);
	    cfg = &config;
	}

	/* Initialize the public address from the config, if any */
	pj_sockaddr_init(pjsip_transport_type_get_af(type), &pub_addr, 
			 NULL, (pj_uint16_t)cfg->port);
	if (cfg->public_addr.slen) {
	    status = pj_sockaddr_set_str_addr(pjsip_transport_type_get_af(type),
					      &pub_addr, &cfg->public_addr);
	    if (status != PJ_SUCCESS) {
		pjsua_perror(THIS_FILE, 
			     "Unable to resolve transport public address", 
			     status);
		goto on_return;
	    }
	}

	/* Create the socket and possibly resolve the address with STUN 
	 * (only when public address is not specified).
	 */
	status = create_sip_udp_sock(pjsip_transport_type_get_af(type),
				     cfg, &sock, &pub_addr);
	if (status != PJ_SUCCESS)
	    goto on_return;

	pj_ansi_strcpy(hostbuf, addr_string(&pub_addr));
	addr_name.host = pj_str(hostbuf);
	addr_name.port = pj_sockaddr_get_port(&pub_addr);

	/* Create UDP transport */
	status = pjsip_udp_transport_attach2(pjsua_var.endpt, type, sock,
					     &addr_name, 1, &tp);
	if (status != PJ_SUCCESS) {
	    pjsua_perror(THIS_FILE, "Error creating SIP UDP transport", 
			 status);
	    pj_sock_close(sock);
	    goto on_return;
	}


	/* Save the transport */
	pjsua_var.tpdata[id].type = type;
	pjsua_var.tpdata[id].local_name = tp->local_name;
	pjsua_var.tpdata[id].data.tp = tp;
	if (cfg->bound_addr.slen)
	    pjsua_var.tpdata[id].has_bound_addr = PJ_TRUE;

#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0

    } else if (type == PJSIP_TRANSPORT_TCP || type == PJSIP_TRANSPORT_TCP6) {
	/*
	 * Create TCP transport.
	 */
	pjsua_transport_config config;
	pjsip_tpfactory *tcp;
	pjsip_tcp_transport_cfg tcp_cfg;
	int af;

	af = (type==PJSIP_TRANSPORT_TCP6) ? pj_AF_INET6() : pj_AF_INET();
	pjsip_tcp_transport_cfg_default(&tcp_cfg, af);

	/* Supply default config if it's not specified */
	if (cfg == NULL) {
	    pjsua_transport_config_default(&config);
	    cfg = &config;
	}

	/* Configure bind address */
	if (cfg->port)
	    pj_sockaddr_set_port(&tcp_cfg.bind_addr, (pj_uint16_t)cfg->port);

	if (cfg->bound_addr.slen) {
	    status = pj_sockaddr_set_str_addr(tcp_cfg.af, 
					      &tcp_cfg.bind_addr,
					      &cfg->bound_addr);
	    if (status != PJ_SUCCESS) {
		pjsua_perror(THIS_FILE, 
			     "Unable to resolve transport bound address", 
			     status);
		goto on_return;
	    }
	}

	/* Set published name */
	if (cfg->public_addr.slen)
	    tcp_cfg.addr_name.host = cfg->public_addr;

	/* Copy the QoS settings */
	tcp_cfg.qos_type = cfg->qos_type;
	pj_memcpy(&tcp_cfg.qos_params, &cfg->qos_params, 
		  sizeof(cfg->qos_params));

	/* Copy the sockopt */
	pj_memcpy(&tcp_cfg.sockopt_params, &cfg->sockopt_params,
		  sizeof(tcp_cfg.sockopt_params));

	/* Create the TCP transport */
	status = pjsip_tcp_transport_start3(pjsua_var.endpt, &tcp_cfg, &tcp);

	if (status != PJ_SUCCESS) {
	    pjsua_perror(THIS_FILE, "Error creating SIP TCP listener", 
			 status);
	    goto on_return;
	}

	/* Save the transport */
	pjsua_var.tpdata[id].type = type;
	pjsua_var.tpdata[id].local_name = tcp->addr_name;
	pjsua_var.tpdata[id].data.factory = tcp;

#endif	/* PJ_HAS_TCP */

#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
    } else if (type == PJSIP_TRANSPORT_TLS || type == PJSIP_TRANSPORT_TLS6) {
	/*
	 * Create TLS transport.
	 */
	pjsua_transport_config config;
	pjsip_host_port a_name;
	pjsip_tpfactory *tls;
	pj_sockaddr local_addr;
	int af;

	/* Supply default config if it's not specified */
	if (cfg == NULL) {
	    pjsua_transport_config_default(&config);
	    config.port = 5061;
	    cfg = &config;
	}

	/* Init local address */
	af = (type==PJSIP_TRANSPORT_TLS) ? pj_AF_INET() : pj_AF_INET6();
	pj_sockaddr_init(af, &local_addr, NULL, 0);

	if (cfg->port)
	    pj_sockaddr_set_port(&local_addr, (pj_uint16_t)cfg->port);

	if (cfg->bound_addr.slen) {
	    status = pj_sockaddr_set_str_addr(af, &local_addr,
	                                      &cfg->bound_addr);
	    if (status != PJ_SUCCESS) {
		pjsua_perror(THIS_FILE, 
			     "Unable to resolve transport bound address", 
			     status);
		goto on_return;
	    }
	}

	/* Init published name */
	pj_bzero(&a_name, sizeof(pjsip_host_port));
	if (cfg->public_addr.slen)
	    a_name.host = cfg->public_addr;

	status = pjsip_tls_transport_start2(pjsua_var.endpt, &cfg->tls_setting,
					    &local_addr, &a_name, 1, &tls);
	if (status != PJ_SUCCESS) {
	    pjsua_perror(THIS_FILE, "Error creating SIP TLS listener", 
			 status);
	    goto on_return;
	}

	/* Save the transport */
	pjsua_var.tpdata[id].type = type;
	pjsua_var.tpdata[id].local_name = tls->addr_name;
	pjsua_var.tpdata[id].data.factory = tls;
#endif

    } else {
	status = PJSIP_EUNSUPTRANSPORT;
	pjsua_perror(THIS_FILE, "Error creating transport", status);
	goto on_return;
    }

    /* Set transport state callback */
    set_tp_state_cb();

    /* Return the ID */
    if (p_id) *p_id = id;

    status = PJ_SUCCESS;

on_return:

    PJSUA_UNLOCK();

    return status;
}