/* Notification from ioqueue about incoming RTCP packet */ static void on_rx_rtcp(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { struct transport_udp *udp; pj_status_t status; PJ_UNUSED_ARG(op_key); udp = (struct transport_udp*) pj_ioqueue_get_user_data(key); do { void (*cb)(void*,void*,pj_ssize_t); void *user_data; cb = udp->rtcp_cb; user_data = udp->user_data; if (udp->attached && cb) (*cb)(user_data, udp->rtcp_pkt, bytes_read); /* Check if RTCP source address is the same as the configured * remote address, and switch the address when they are * different. */ if (bytes_read>0 && (udp->options & PJMEDIA_UDP_NO_SRC_ADDR_CHECKING)==0) { if (pj_sockaddr_cmp(&udp->rem_rtcp_addr, &udp->rtcp_src_addr) == 0) { /* Still receiving from rem_rtcp_addr, don't switch */ udp->rtcp_src_cnt = 0; } else { ++udp->rtcp_src_cnt; if (udp->rtcp_src_cnt >= PJMEDIA_RTCP_NAT_PROBATION_CNT ) { char addr_text[80]; udp->rtcp_src_cnt = 0; pj_memcpy(&udp->rem_rtcp_addr, &udp->rtcp_src_addr, sizeof(pj_sockaddr)); PJ_LOG(4,(udp->base.name, "Remote RTCP address switched to %s", pj_sockaddr_print(&udp->rtcp_src_addr, addr_text, sizeof(addr_text), 3))); } } } bytes_read = sizeof(udp->rtcp_pkt); udp->rtcp_addr_len = sizeof(udp->rtcp_src_addr); status = pj_ioqueue_recvfrom(udp->rtcp_key, &udp->rtcp_read_op, udp->rtcp_pkt, &bytes_read, 0, &udp->rtcp_src_addr, &udp->rtcp_addr_len); if (status != PJ_EPENDING && status != PJ_SUCCESS) bytes_read = -status; } while (status != PJ_EPENDING && status != PJ_ECANCELLED); }
/* Start ioqueue asynchronous reading to all rdata */ static pj_status_t start_async_read(struct udp_transport *tp) { int i; pj_status_t status; /* Start reading the ioqueue. */ for (i=0; i<tp->rdata_cnt; ++i) { pj_ssize_t size; size = sizeof(tp->rdata[i]->pkt_info.packet); tp->rdata[i]->pkt_info.src_addr_len = sizeof(tp->rdata[i]->pkt_info.src_addr); status = pj_ioqueue_recvfrom(tp->key, &tp->rdata[i]->tp_info.op_key.op_key, tp->rdata[i]->pkt_info.packet, &size, PJ_IOQUEUE_ALWAYS_ASYNC, &tp->rdata[i]->pkt_info.src_addr, &tp->rdata[i]->pkt_info.src_addr_len); if (status == PJ_SUCCESS) { pj_assert(!"Shouldn't happen because PJ_IOQUEUE_ALWAYS_ASYNC!"); udp_on_read_complete(tp->key, &tp->rdata[i]->tp_info.op_key.op_key, size); } else if (status != PJ_EPENDING) { /* Error! */ return status; } } return PJ_SUCCESS; }
/* * ioqueue notification on RX packets from the relay socket. */ static void on_rx_from_peer(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { pj_turn_relay_res *rel; pj_status_t status; rel = (pj_turn_relay_res*) pj_ioqueue_get_user_data(key); /* Lock the allocation */ pj_lock_acquire(rel->allocation->lock); do { if (bytes_read > 0) { handle_peer_pkt(rel->allocation, rel, rel->tp.rx_pkt, bytes_read, &rel->tp.src_addr); } /* Read next packet */ bytes_read = sizeof(rel->tp.rx_pkt); rel->tp.src_addr_len = sizeof(rel->tp.src_addr); status = pj_ioqueue_recvfrom(key, op_key, rel->tp.rx_pkt, &bytes_read, 0, &rel->tp.src_addr, &rel->tp.src_addr_len); if (status != PJ_EPENDING && status != PJ_SUCCESS) bytes_read = -status; } while (status != PJ_EPENDING && status != PJ_ECANCELLED); /* Release allocation lock */ pj_lock_release(rel->allocation->lock); }
// // Start async recvfrom() // pj_status_t recvfrom( Pj_Async_Op *op_key, void *buf, pj_ssize_t *len, unsigned flags, Pj_Inet_Addr *addr) { addr->addrlen_ = sizeof(Pj_Inet_Addr); return pj_ioqueue_recvfrom( key_, op_key, buf, len, flags, addr, &addr->addrlen_ ); }
/* Notification from ioqueue about incoming RTCP packet */ static void on_rx_rtcp(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { struct transport_udp *udp; pj_status_t status; PJ_UNUSED_ARG(op_key); udp = pj_ioqueue_get_user_data(key); do { void (*cb)(void*,const void*,pj_ssize_t); void *user_data; cb = udp->rtcp_cb; user_data = udp->user_data; if (udp->attached && cb) (*cb)(user_data, udp->rtcp_pkt, bytes_read); /* Check if RTCP source address is the same as the configured * remote address, and switch the address when they are * different. */ if (bytes_read>0 && (udp->options & PJMEDIA_UDP_NO_SRC_ADDR_CHECKING)==0 && ((udp->rem_rtcp_addr.sin_addr.s_addr != udp->rtcp_src_addr.sin_addr.s_addr) || (udp->rem_rtcp_addr.sin_port != udp->rtcp_src_addr.sin_port))) { pj_memcpy(&udp->rem_rtcp_addr, &udp->rtcp_src_addr, sizeof(pj_sockaddr_in)); PJ_LOG(4,(udp->base.name, "Remote RTCP address switched to %s:%d", pj_inet_ntoa(udp->rtcp_src_addr.sin_addr), pj_ntohs(udp->rtcp_src_addr.sin_port))); } bytes_read = sizeof(udp->rtcp_pkt); udp->rtcp_addr_len = sizeof(udp->rtcp_src_addr); status = pj_ioqueue_recvfrom(udp->rtcp_key, &udp->rtcp_read_op, udp->rtcp_pkt, &bytes_read, 0, &udp->rtcp_src_addr, &udp->rtcp_addr_len); if (status != PJ_EPENDING && status != PJ_SUCCESS) bytes_read = -status; } while (status != PJ_EPENDING); }
/* * Callback upon receiving packet from network. */ static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { nat_detect_session *sess; pj_status_t status; sess = (nat_detect_session *) pj_ioqueue_get_user_data(key); pj_assert(sess != NULL); pj_grp_lock_acquire(sess->grp_lock); /* Ignore packet when STUN session has been destroyed */ if (!sess->stun_sess) goto on_return; if (bytes_read < 0) { if (-bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) && -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) { /* Permanent error */ end_session(sess, (pj_status_t)-bytes_read, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); goto on_return; } } else if (bytes_read > 0) { pj_stun_session_on_rx_pkt(sess->stun_sess, sess->rx_pkt, bytes_read, PJ_STUN_IS_DATAGRAM|PJ_STUN_CHECK_PACKET, NULL, NULL, &sess->src_addr, sess->src_addr_len); } sess->rx_pkt_len = sizeof(sess->rx_pkt); sess->src_addr_len = sizeof(sess->src_addr); status = pj_ioqueue_recvfrom(key, op_key, sess->rx_pkt, &sess->rx_pkt_len, PJ_IOQUEUE_ALWAYS_ASYNC, &sess->src_addr, &sess->src_addr_len); if (status != PJ_EPENDING) { pj_assert(status != PJ_SUCCESS); end_session(sess, status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); } on_return: pj_grp_lock_release(sess->grp_lock); }
/* * Callback on received packet. */ static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { struct udp_listener *udp; struct read_op *read_op = (struct read_op*) op_key; pj_status_t status; udp = (struct udp_listener*) pj_ioqueue_get_user_data(key); do { pj_pool_t *rpool; /* Report to server */ if (bytes_read > 0) { read_op->pkt.len = bytes_read; pj_gettimeofday(&read_op->pkt.rx_time); pj_turn_srv_on_rx_pkt(udp->base.server, &read_op->pkt); } /* Reset pool */ rpool = read_op->pkt.pool; pj_pool_reset(rpool); read_op->pkt.pool = rpool; read_op->pkt.transport = &udp->tp; read_op->pkt.src.tp_type = udp->base.tp_type; /* Read next packet */ bytes_read = sizeof(read_op->pkt.pkt); read_op->pkt.src_addr_len = sizeof(read_op->pkt.src.clt_addr); pj_bzero(&read_op->pkt.src.clt_addr, sizeof(read_op->pkt.src.clt_addr)); status = pj_ioqueue_recvfrom(udp->key, op_key, read_op->pkt.pkt, &bytes_read, 0, &read_op->pkt.src.clt_addr, &read_op->pkt.src_addr_len); if (status != PJ_EPENDING && status != PJ_SUCCESS) bytes_read = -status; } while (status != PJ_EPENDING && status != PJ_ECANCELLED && status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)); }
/* Initialize UDP socket */ static pj_status_t init_sock(pj_dns_resolver *resv) { pj_ioqueue_callback socket_cb; pj_status_t status; /* Create the UDP socket */ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &resv->udp_sock); if (status != PJ_SUCCESS) return status; /* Bind to any address/port */ status = pj_sock_bind_in(resv->udp_sock, 0, 0); if (status != PJ_SUCCESS) return status; /* Register to ioqueue */ pj_bzero(&socket_cb, sizeof(socket_cb)); socket_cb.on_read_complete = &on_read_complete; status = pj_ioqueue_register_sock(resv->pool, resv->ioqueue, resv->udp_sock, resv, &socket_cb, &resv->udp_key); if (status != PJ_SUCCESS) return status; pj_ioqueue_op_key_init(&resv->udp_op_rx_key, sizeof(resv->udp_op_rx_key)); pj_ioqueue_op_key_init(&resv->udp_op_tx_key, sizeof(resv->udp_op_tx_key)); /* Start asynchronous read to the UDP socket */ resv->udp_len = sizeof(resv->udp_rx_pkt); resv->udp_addr_len = sizeof(resv->udp_src_addr); status = pj_ioqueue_recvfrom(resv->udp_key, &resv->udp_op_rx_key, resv->udp_rx_pkt, &resv->udp_len, PJ_IOQUEUE_ALWAYS_ASYNC, &resv->udp_src_addr, &resv->udp_addr_len); if (status != PJ_EPENDING) return status; return PJ_SUCCESS; }
PJ_DEF(pj_status_t) pj_activesock_start_recvfrom2( pj_activesock_t *asock, pj_pool_t *pool, unsigned buff_size, void *readbuf[], pj_uint32_t flags) { unsigned i; pj_status_t status; PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL); PJ_ASSERT_RETURN(asock->read_type == TYPE_NONE, PJ_EINVALIDOP); asock->read_op = (struct read_op*) pj_pool_calloc(pool, asock->async_count, sizeof(struct read_op)); asock->read_type = TYPE_RECV_FROM; asock->read_flags = flags; for (i=0; i<asock->async_count; ++i) { struct read_op *r = &asock->read_op[i]; pj_ssize_t size_to_read; r->pkt = (pj_uint8_t*) readbuf[i]; size_to_read = r->max_size = buff_size; r->src_addr_len = sizeof(r->src_addr); status = pj_ioqueue_recvfrom(asock->key, &r->op_key, r->pkt, &size_to_read, PJ_IOQUEUE_ALWAYS_ASYNC | flags, &r->src_addr, &r->src_addr_len); PJ_ASSERT_RETURN(status != PJ_SUCCESS, PJ_EBUG); if (status != PJ_EPENDING) return status; } return PJ_SUCCESS; }
static int worker_thread(void *arg) { pj_ioqueue_t *ioqueue = (pj_ioqueue_t*) arg; struct op_key read_op, write_op; char recv_buf[512], send_buf[512]; pj_ssize_t length; pj_status_t rc; read_op.peer = &write_op; read_op.is_pending = 0; read_op.last_err = 0; read_op.buffer = recv_buf; read_op.size = sizeof(recv_buf); read_op.addrlen = sizeof(read_op.addr); write_op.peer = &read_op; write_op.is_pending = 0; write_op.last_err = 0; write_op.buffer = send_buf; write_op.size = sizeof(send_buf); length = sizeof(recv_buf); rc = pj_ioqueue_recvfrom(key, &read_op.op_key_, recv_buf, &length, 0, &read_op.addr, &read_op.addrlen); if (rc == PJ_SUCCESS) { read_op.is_pending = 1; on_read_complete(key, &read_op.op_key_, length); } while (!thread_quit_flag) { pj_time_val timeout; timeout.sec = 0; timeout.msec = 10; rc = pj_ioqueue_poll(ioqueue, &timeout); } return 0; }
/** * Create UDP stream transport from existing socket info. */ PJ_DEF(pj_status_t) pjmedia_transport_udp_attach( pjmedia_endpt *endpt, const char *name, const pjmedia_sock_info *si, unsigned options, pjmedia_transport **p_tp) { struct transport_udp *tp; pj_pool_t *pool; pj_ioqueue_t *ioqueue; pj_ioqueue_callback rtp_cb, rtcp_cb; pj_ssize_t size; unsigned i; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && si && p_tp, PJ_EINVAL); /* Get ioqueue instance */ ioqueue = pjmedia_endpt_get_ioqueue(endpt); if (name==NULL) name = "udp%p"; /* Create transport structure */ pool = pjmedia_endpt_create_pool(endpt, name, 512, 512); if (!pool) return PJ_ENOMEM; tp = PJ_POOL_ZALLOC_T(pool, struct transport_udp); tp->pool = pool; tp->options = options; pj_memcpy(tp->base.name, pool->obj_name, PJ_MAX_OBJ_NAME); tp->base.op = &transport_udp_op; tp->base.type = PJMEDIA_TRANSPORT_TYPE_UDP; /* Copy socket infos */ tp->rtp_sock = si->rtp_sock; tp->rtp_addr_name = si->rtp_addr_name; tp->rtcp_sock = si->rtcp_sock; tp->rtcp_addr_name = si->rtcp_addr_name; /* If address is 0.0.0.0, use host's IP address */ if (!pj_sockaddr_has_addr(&tp->rtp_addr_name)) { pj_sockaddr hostip; status = pj_gethostip(tp->rtp_addr_name.addr.sa_family, &hostip); if (status != PJ_SUCCESS) goto on_error; pj_memcpy(pj_sockaddr_get_addr(&tp->rtp_addr_name), pj_sockaddr_get_addr(&hostip), pj_sockaddr_get_addr_len(&hostip)); } /* Same with RTCP */ if (!pj_sockaddr_has_addr(&tp->rtcp_addr_name)) { pj_memcpy(pj_sockaddr_get_addr(&tp->rtcp_addr_name), pj_sockaddr_get_addr(&tp->rtp_addr_name), pj_sockaddr_get_addr_len(&tp->rtp_addr_name)); } /* Setup RTP socket with the ioqueue */ pj_bzero(&rtp_cb, sizeof(rtp_cb)); rtp_cb.on_read_complete = &on_rx_rtp; status = pj_ioqueue_register_sock(pool, ioqueue, tp->rtp_sock, tp, &rtp_cb, &tp->rtp_key); if (status != PJ_SUCCESS) goto on_error; /* Disallow concurrency so that detach() and destroy() are * synchronized with the callback. */ status = pj_ioqueue_set_concurrency(tp->rtp_key, PJ_FALSE); if (status != PJ_SUCCESS) goto on_error; pj_ioqueue_op_key_init(&tp->rtp_read_op, sizeof(tp->rtp_read_op)); for (i=0; i<PJ_ARRAY_SIZE(tp->rtp_pending_write); ++i) pj_ioqueue_op_key_init(&tp->rtp_pending_write[i].op_key, sizeof(tp->rtp_pending_write[i].op_key)); /* Kick of pending RTP read from the ioqueue */ tp->rtp_addrlen = sizeof(tp->rtp_src_addr); size = sizeof(tp->rtp_pkt); status = pj_ioqueue_recvfrom(tp->rtp_key, &tp->rtp_read_op, tp->rtp_pkt, &size, PJ_IOQUEUE_ALWAYS_ASYNC, &tp->rtp_src_addr, &tp->rtp_addrlen); if (status != PJ_EPENDING) goto on_error; /* Setup RTCP socket with ioqueue */ pj_bzero(&rtcp_cb, sizeof(rtcp_cb)); rtcp_cb.on_read_complete = &on_rx_rtcp; status = pj_ioqueue_register_sock(pool, ioqueue, tp->rtcp_sock, tp, &rtcp_cb, &tp->rtcp_key); if (status != PJ_SUCCESS) goto on_error; status = pj_ioqueue_set_concurrency(tp->rtcp_key, PJ_FALSE); if (status != PJ_SUCCESS) goto on_error; pj_ioqueue_op_key_init(&tp->rtcp_read_op, sizeof(tp->rtcp_read_op)); pj_ioqueue_op_key_init(&tp->rtcp_write_op, sizeof(tp->rtcp_write_op)); /* Kick of pending RTCP read from the ioqueue */ size = sizeof(tp->rtcp_pkt); tp->rtcp_addr_len = sizeof(tp->rtcp_src_addr); status = pj_ioqueue_recvfrom( tp->rtcp_key, &tp->rtcp_read_op, tp->rtcp_pkt, &size, PJ_IOQUEUE_ALWAYS_ASYNC, &tp->rtcp_src_addr, &tp->rtcp_addr_len); if (status != PJ_EPENDING) goto on_error; /* Done */ *p_tp = &tp->base; return PJ_SUCCESS; on_error: transport_destroy(&tp->base); return status; }
/* Notification from ioqueue about incoming RTP packet */ static void on_rx_rtp( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { struct transport_udp *udp; pj_status_t status; PJ_UNUSED_ARG(op_key); udp = pj_ioqueue_get_user_data(key); do { void (*cb)(void*,const void*,pj_ssize_t); void *user_data; cb = udp->rtp_cb; user_data = udp->user_data; /* Simulate packet lost on RX direction */ if (udp->rx_drop_pct) { if ((pj_rand() % 100) <= (int)udp->rx_drop_pct) { PJ_LOG(5,(udp->base.name, "RX RTP packet dropped because of pkt lost " "simulation")); goto read_next_packet; } } if (udp->attached && cb) (*cb)(user_data, udp->rtp_pkt, bytes_read); /* See if source address of RTP packet is different than the * configured address, and switch RTP remote address to * source packet address after several consecutive packets * have been received. */ if (bytes_read>0 && (udp->options & PJMEDIA_UDP_NO_SRC_ADDR_CHECKING)==0) { if ((udp->rem_rtp_addr.sin_addr.s_addr != udp->rtp_src_addr.sin_addr.s_addr) || (udp->rem_rtp_addr.sin_port != udp->rtp_src_addr.sin_port)) { udp->rtp_src_cnt++; if (udp->rtp_src_cnt >= PJMEDIA_RTP_NAT_PROBATION_CNT) { /* Set remote RTP address to source address */ udp->rem_rtp_addr = udp->rtp_src_addr; /* Reset counter */ udp->rtp_src_cnt = 0; PJ_LOG(4,(udp->base.name, "Remote RTP address switched to %s:%d", pj_inet_ntoa(udp->rtp_src_addr.sin_addr), pj_ntohs(udp->rtp_src_addr.sin_port))); /* Also update remote RTCP address if actual RTCP source * address is not heard yet. */ if (udp->rtcp_src_addr.sin_addr.s_addr == 0) { pj_uint16_t port; pj_memcpy(&udp->rem_rtcp_addr, &udp->rem_rtp_addr, sizeof(pj_sockaddr_in)); port = (pj_uint16_t) (pj_ntohs(udp->rem_rtp_addr.sin_port)+1); udp->rem_rtcp_addr.sin_port = pj_htons(port); pj_memcpy(&udp->rtcp_src_addr, &udp->rem_rtcp_addr, sizeof(pj_sockaddr_in)); PJ_LOG(4,(udp->base.name, "Remote RTCP address switched to %s:%d", pj_inet_ntoa(udp->rtcp_src_addr.sin_addr), pj_ntohs(udp->rtcp_src_addr.sin_port))); } } } } read_next_packet: bytes_read = sizeof(udp->rtp_pkt); udp->rtp_addrlen = sizeof(pj_sockaddr_in); status = pj_ioqueue_recvfrom(udp->rtp_key, &udp->rtp_read_op, udp->rtp_pkt, &bytes_read, 0, &udp->rtp_src_addr, &udp->rtp_addrlen); if (status != PJ_EPENDING && status != PJ_SUCCESS) bytes_read = -status; } while (status != PJ_EPENDING); }
/** * Create UDP stream transport from existing socket info. */ PJ_DEF(pj_status_t) pjmedia_transport_udp_attach( pjmedia_endpt *endpt, const char *name, const pjmedia_sock_info *si, unsigned options, pjmedia_transport **p_tp) { struct transport_udp *tp; pj_pool_t *pool; pj_ioqueue_t *ioqueue; pj_ioqueue_callback rtp_cb, rtcp_cb; pj_ssize_t size; unsigned i; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && si && p_tp, PJ_EINVAL); /* Check name */ if (!name) name = "udpmedia"; /* Get ioqueue instance */ ioqueue = pjmedia_endpt_get_ioqueue(endpt); /* Create transport structure */ pool = pjmedia_endpt_create_pool(endpt, name, 4000, 4000); if (!pool) return PJ_ENOMEM; tp = pj_pool_zalloc(pool, sizeof(struct transport_udp)); tp->pool = pool; tp->options = options; pj_ansi_strcpy(tp->base.name, name); tp->base.op = &transport_udp_op; /* Copy socket infos */ tp->rtp_sock = si->rtp_sock; tp->rtp_addr_name = si->rtp_addr_name; tp->rtcp_sock = si->rtcp_sock; tp->rtcp_addr_name = si->rtcp_addr_name; /* If address is 0.0.0.0, use host's IP address */ if (tp->rtp_addr_name.sin_addr.s_addr == 0) { pj_in_addr hostip; status = pj_gethostip(&hostip); if (status != PJ_SUCCESS) goto on_error; tp->rtp_addr_name.sin_addr = hostip; } /* Same with RTCP */ if (tp->rtcp_addr_name.sin_addr.s_addr == 0) { tp->rtcp_addr_name.sin_addr.s_addr = tp->rtp_addr_name.sin_addr.s_addr; } /* Setup RTP socket with the ioqueue */ pj_bzero(&rtp_cb, sizeof(rtp_cb)); rtp_cb.on_read_complete = &on_rx_rtp; status = pj_ioqueue_register_sock(pool, ioqueue, tp->rtp_sock, tp, &rtp_cb, &tp->rtp_key); if (status != PJ_SUCCESS) goto on_error; pj_ioqueue_op_key_init(&tp->rtp_read_op, sizeof(tp->rtp_read_op)); for (i=0; i<PJ_ARRAY_SIZE(tp->rtp_pending_write); ++i) pj_ioqueue_op_key_init(&tp->rtp_pending_write[i].op_key, sizeof(tp->rtp_pending_write[i].op_key)); /* Kick of pending RTP read from the ioqueue */ tp->rtp_addrlen = sizeof(tp->rtp_src_addr); size = sizeof(tp->rtp_pkt); status = pj_ioqueue_recvfrom(tp->rtp_key, &tp->rtp_read_op, tp->rtp_pkt, &size, PJ_IOQUEUE_ALWAYS_ASYNC, &tp->rtp_src_addr, &tp->rtp_addrlen); if (status != PJ_EPENDING) goto on_error; /* Setup RTCP socket with ioqueue */ pj_bzero(&rtcp_cb, sizeof(rtcp_cb)); rtcp_cb.on_read_complete = &on_rx_rtcp; status = pj_ioqueue_register_sock(pool, ioqueue, tp->rtcp_sock, tp, &rtcp_cb, &tp->rtcp_key); if (status != PJ_SUCCESS) goto on_error; pj_ioqueue_op_key_init(&tp->rtcp_read_op, sizeof(tp->rtcp_read_op)); pj_ioqueue_op_key_init(&tp->rtcp_write_op, sizeof(tp->rtcp_write_op)); /* Kick of pending RTCP read from the ioqueue */ size = sizeof(tp->rtcp_pkt); tp->rtcp_addr_len = sizeof(tp->rtcp_src_addr); status = pj_ioqueue_recvfrom( tp->rtcp_key, &tp->rtcp_read_op, tp->rtcp_pkt, &size, PJ_IOQUEUE_ALWAYS_ASYNC, &tp->rtcp_src_addr, &tp->rtcp_addr_len); if (status != PJ_EPENDING) goto on_error; /* Done */ *p_tp = &tp->base; return PJ_SUCCESS; on_error: pjmedia_transport_udp_close(&tp->base); return status; }
static void ioqueue_on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { pj_activesock_t *asock; struct read_op *r = (struct read_op*)op_key; unsigned loop = 0; pj_status_t status; asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key); /* Ignore if we've been shutdown */ if (asock->shutdown & SHUT_RX) return; do { unsigned flags; if (bytes_read > 0) { /* * We've got new data. */ pj_size_t remainder; pj_bool_t ret; /* Append this new data to existing data. If socket is stream * oriented, user might have left some data in the buffer. * Otherwise if socket is datagram there will be nothing in * existing packet hence the packet will contain only the new * packet. */ r->size += bytes_read; /* Set default remainder to zero */ remainder = 0; /* And return value to TRUE */ ret = PJ_TRUE; /* Notify callback */ if (asock->read_type == TYPE_RECV && asock->cb.on_data_read) { ret = (*asock->cb.on_data_read)(asock, r->pkt, r->size, PJ_SUCCESS, &remainder); } else if (asock->read_type == TYPE_RECV_FROM && asock->cb.on_data_recvfrom) { ret = (*asock->cb.on_data_recvfrom)(asock, r->pkt, r->size, &r->src_addr, r->src_addr_len, PJ_SUCCESS); } /* If callback returns false, we have been destroyed! */ if (!ret) return; /* Only stream oriented socket may leave data in the packet */ if (asock->stream_oriented) { r->size = remainder; } else { r->size = 0; } } else if (bytes_read <= 0 && -bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) && -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && (asock->stream_oriented || -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET))) { pj_size_t remainder; pj_bool_t ret; if (bytes_read == 0) { /* For stream/connection oriented socket, this means the * connection has been closed. For datagram sockets, it means * we've received datagram with zero length. */ if (asock->stream_oriented) status = PJ_EEOF; else status = PJ_SUCCESS; } else { /* This means we've got an error. If this is stream/connection * oriented, it means connection has been closed. For datagram * sockets, it means we've got some error (e.g. EWOULDBLOCK). */ status = (pj_status_t)-bytes_read; } /* Set default remainder to zero */ remainder = 0; /* And return value to TRUE */ ret = PJ_TRUE; /* Notify callback */ if (asock->read_type == TYPE_RECV && asock->cb.on_data_read) { /* For connection oriented socket, we still need to report * the remainder data (if any) to the user to let user do * processing with the remainder data before it closes the * connection. * If there is no remainder data, set the packet to NULL. */ /* Shouldn't set the packet to NULL, as there may be active * socket user, such as SSL socket, that needs to have access * to the read buffer packet. */ //ret = (*asock->cb.on_data_read)(asock, (r->size? r->pkt:NULL), // r->size, status, &remainder); ret = (*asock->cb.on_data_read)(asock, r->pkt, r->size, status, &remainder); } else if (asock->read_type == TYPE_RECV_FROM && asock->cb.on_data_recvfrom) { /* This would always be datagram oriented hence there's * nothing in the packet. We can't be sure if there will be * anything useful in the source_addr, so just put NULL * there too. */ /* In some scenarios, status may be PJ_SUCCESS. The upper * layer application may not expect the callback to be called * with successful status and NULL data, so lets not call the * callback if the status is PJ_SUCCESS. */ if (status != PJ_SUCCESS ) { ret = (*asock->cb.on_data_recvfrom)(asock, NULL, 0, NULL, 0, status); } } /* If callback returns false, we have been destroyed! */ if (!ret) return; /* Also stop further read if we've been shutdown */ if (asock->shutdown & SHUT_RX) return; /* Only stream oriented socket may leave data in the packet */ if (asock->stream_oriented) { r->size = remainder; } else { r->size = 0; } } /* Read next data. We limit ourselves to processing max_loop immediate * data, so when the loop counter has exceeded this value, force the * read()/recvfrom() to return pending operation to allow the program * to do other jobs. */ bytes_read = r->max_size - r->size; flags = asock->read_flags; if (++loop >= asock->max_loop) flags |= PJ_IOQUEUE_ALWAYS_ASYNC; if (asock->read_type == TYPE_RECV) { status = pj_ioqueue_recv(key, op_key, r->pkt + r->size, &bytes_read, flags); } else { r->src_addr_len = sizeof(r->src_addr); status = pj_ioqueue_recvfrom(key, op_key, r->pkt + r->size, &bytes_read, flags, &r->src_addr, &r->src_addr_len); } if (status == PJ_SUCCESS) { /* Immediate data */ ; } else if (status != PJ_EPENDING && status != PJ_ECANCELLED) { /* Error */ bytes_read = -status; } else { break; } } while (1); }
/* * udp_on_read_complete() * * This is callback notification from ioqueue that a pending recvfrom() * operation has completed. */ static void udp_on_read_complete( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { /* See https://trac.pjsip.org/repos/ticket/1197 */ enum { MAX_IMMEDIATE_PACKET = 50 }; pjsip_rx_data_op_key *rdata_op_key = (pjsip_rx_data_op_key*) op_key; pjsip_rx_data *rdata = rdata_op_key->rdata; struct udp_transport *tp = (struct udp_transport*)rdata->tp_info.transport; int i; pj_status_t status; /* Don't do anything if transport is closing. */ if (tp->is_closing) { tp->is_closing++; return; } /* Don't do anything if transport is being paused. */ if (tp->is_paused) return; /* * The idea of the loop is to process immediate data received by * pj_ioqueue_recvfrom(), as long as i < MAX_IMMEDIATE_PACKET. When * i is >= MAX_IMMEDIATE_PACKET, we force the recvfrom() operation to * complete asynchronously, to allow other sockets to get their data. */ for (i=0;; ++i) { enum { MIN_SIZE = 32 }; pj_uint32_t flags; /* Report the packet to transport manager. Only do so if packet size * is relatively big enough for a SIP packet. */ if (bytes_read > MIN_SIZE) { pj_ssize_t size_eaten; const pj_sockaddr *src_addr = &rdata->pkt_info.src_addr; /* Init pkt_info part. */ rdata->pkt_info.len = bytes_read; rdata->pkt_info.zero = 0; pj_gettimeofday(&rdata->pkt_info.timestamp); if (src_addr->addr.sa_family == pj_AF_INET()) { pj_ansi_strcpy(rdata->pkt_info.src_name, pj_inet_ntoa(src_addr->ipv4.sin_addr)); rdata->pkt_info.src_port = pj_ntohs(src_addr->ipv4.sin_port); } else { pj_inet_ntop(pj_AF_INET6(), pj_sockaddr_get_addr(&rdata->pkt_info.src_addr), rdata->pkt_info.src_name, sizeof(rdata->pkt_info.src_name)); rdata->pkt_info.src_port = pj_ntohs(src_addr->ipv6.sin6_port); } size_eaten = pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, rdata); if (size_eaten < 0) { pj_assert(!"It shouldn't happen!"); size_eaten = rdata->pkt_info.len; } /* Since this is UDP, the whole buffer is the message. */ rdata->pkt_info.len = 0; } else if (bytes_read <= MIN_SIZE) { /* TODO: */ } else if (-bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) && -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) { /* Report error to endpoint. */ PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt, rdata->tp_info.transport->obj_name, (pj_status_t)-bytes_read, "Warning: pj_ioqueue_recvfrom()" " callback error")); } if (i >= MAX_IMMEDIATE_PACKET) { /* Force ioqueue_recvfrom() to return PJ_EPENDING */ flags = PJ_IOQUEUE_ALWAYS_ASYNC; } else { flags = 0; } /* Reset pool. * Need to copy rdata fields to temp variable because they will * be invalid after pj_pool_reset(). */ { pj_pool_t *rdata_pool = rdata->tp_info.pool; struct udp_transport *rdata_tp ; unsigned rdata_index; rdata_tp = (struct udp_transport*)rdata->tp_info.transport; rdata_index = (unsigned)(unsigned long)(pj_ssize_t) rdata->tp_info.tp_data; pj_pool_reset(rdata_pool); init_rdata(rdata_tp, rdata_index, rdata_pool, &rdata); /* Change some vars to point to new location after * pool reset. */ op_key = &rdata->tp_info.op_key.op_key; } /* Only read next packet if transport is not being paused. This * check handles the case where transport is paused while endpoint * is still processing a SIP message. */ if (tp->is_paused) return; /* Read next packet. */ bytes_read = sizeof(rdata->pkt_info.packet); rdata->pkt_info.src_addr_len = sizeof(rdata->pkt_info.src_addr); status = pj_ioqueue_recvfrom(key, op_key, rdata->pkt_info.packet, &bytes_read, flags, &rdata->pkt_info.src_addr, &rdata->pkt_info.src_addr_len); if (status == PJ_SUCCESS) { /* Continue loop. */ pj_assert(i < MAX_IMMEDIATE_PACKET); } else if (status == PJ_EPENDING) { break; } else { if (i < MAX_IMMEDIATE_PACKET) { /* Report error to endpoint if this is not EWOULDBLOCK error.*/ if (status != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) && status != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && status != PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) { PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt, rdata->tp_info.transport->obj_name, status, "Warning: pj_ioqueue_recvfrom")); } /* Continue loop. */ bytes_read = 0; } else { /* This is fatal error. * Ioqueue operation will stop for this transport! */ PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt, rdata->tp_info.transport->obj_name, status, "FATAL: pj_ioqueue_recvfrom() error, " "UDP transport stopping! Error")); break; } } } }
/* * compliance_test() * To test that the basic IOQueue functionality works. It will just exchange * data between two sockets. */ static int compliance_test(pj_bool_t allow_concur) { pj_sock_t ssock=-1, csock=-1; pj_sockaddr_in addr, dst_addr; int addrlen; pj_pool_t *pool = NULL; char *send_buf, *recv_buf; pj_ioqueue_t *ioque = NULL; pj_ioqueue_key_t *skey = NULL, *ckey = NULL; pj_ioqueue_op_key_t read_op, write_op; int bufsize = BUF_MIN_SIZE; pj_ssize_t bytes; int status = -1; pj_str_t temp; pj_bool_t send_pending, recv_pending; pj_status_t rc; pj_set_os_error(PJ_SUCCESS); // Create pool. pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL); // Allocate buffers for send and receive. send_buf = (char*)pj_pool_alloc(pool, bufsize); recv_buf = (char*)pj_pool_alloc(pool, bufsize); // Allocate sockets for sending and receiving. TRACE_("creating sockets..."); rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &ssock); if (rc==PJ_SUCCESS) rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &csock); else csock = PJ_INVALID_SOCKET; if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_sock_socket()", rc); status=-1; goto on_error; } // Bind server socket. TRACE_("bind socket..."); pj_bzero(&addr, sizeof(addr)); addr.sin_family = pj_AF_INET(); addr.sin_port = pj_htons(PORT); if (pj_sock_bind(ssock, &addr, sizeof(addr))) { status=-10; goto on_error; } // Create I/O Queue. TRACE_("create ioqueue..."); rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque); if (rc != PJ_SUCCESS) { status=-20; goto on_error; } // Set concurrency TRACE_("set concurrency..."); rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur); if (rc != PJ_SUCCESS) { status=-21; goto on_error; } // Register server and client socket. // We put this after inactivity socket, hopefully this can represent the // worst waiting time. TRACE_("registering first sockets..."); rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, &test_cb, &skey); if (rc != PJ_SUCCESS) { app_perror("...error(10): ioqueue_register error", rc); status=-25; goto on_error; } TRACE_("registering second sockets..."); rc = pj_ioqueue_register_sock( pool, ioque, csock, NULL, &test_cb, &ckey); if (rc != PJ_SUCCESS) { app_perror("...error(11): ioqueue_register error", rc); status=-26; goto on_error; } // Randomize send_buf. pj_create_random_string(send_buf, bufsize); // Register reading from ioqueue. TRACE_("start recvfrom..."); pj_bzero(&addr, sizeof(addr)); addrlen = sizeof(addr); bytes = bufsize; rc = pj_ioqueue_recvfrom(skey, &read_op, recv_buf, &bytes, 0, &addr, &addrlen); if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { app_perror("...error: pj_ioqueue_recvfrom", rc); status=-28; goto on_error; } else if (rc == PJ_EPENDING) { recv_pending = 1; PJ_LOG(3, (THIS_FILE, "......ok: recvfrom returned pending")); } else { PJ_LOG(3, (THIS_FILE, "......error: recvfrom returned immediate ok!")); status=-29; goto on_error; } // Set destination address to send the packet. TRACE_("set destination address..."); temp = pj_str("127.0.0.1"); if ((rc=pj_sockaddr_in_init(&dst_addr, &temp, PORT)) != 0) { app_perror("...error: unable to resolve 127.0.0.1", rc); status=-290; goto on_error; } // Write must return the number of bytes. TRACE_("start sendto..."); bytes = bufsize; rc = pj_ioqueue_sendto(ckey, &write_op, send_buf, &bytes, 0, &dst_addr, sizeof(dst_addr)); if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { app_perror("...error: pj_ioqueue_sendto", rc); status=-30; goto on_error; } else if (rc == PJ_EPENDING) { send_pending = 1; PJ_LOG(3, (THIS_FILE, "......ok: sendto returned pending")); } else { send_pending = 0; PJ_LOG(3, (THIS_FILE, "......ok: sendto returned immediate success")); } // reset callback variables. callback_read_size = callback_write_size = 0; callback_accept_status = callback_connect_status = -2; callback_read_key = callback_write_key = callback_accept_key = callback_connect_key = NULL; callback_read_op = callback_write_op = NULL; // Poll if pending. while (send_pending || recv_pending) { int rc; pj_time_val timeout = { 5, 0 }; TRACE_("poll..."); #ifdef PJ_SYMBIAN rc = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout)); #else rc = pj_ioqueue_poll(ioque, &timeout); #endif if (rc == 0) { PJ_LOG(1,(THIS_FILE, "...ERROR: timed out...")); status=-45; goto on_error; } else if (rc < 0) { app_perror("...ERROR in ioqueue_poll()", -rc); status=-50; goto on_error; } if (callback_read_key != NULL) { if (callback_read_size != bufsize) { status=-61; goto on_error; } if (callback_read_key != skey) { status=-65; goto on_error; } if (callback_read_op != &read_op) { status=-66; goto on_error; } if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) { status=-67; goto on_error; } if (addrlen != sizeof(pj_sockaddr_in)) { status=-68; goto on_error; } if (addr.sin_family != pj_AF_INET()) { status=-69; goto on_error; } recv_pending = 0; } if (callback_write_key != NULL) { if (callback_write_size != bufsize) { status=-73; goto on_error; } if (callback_write_key != ckey) { status=-75; goto on_error; } if (callback_write_op != &write_op) { status=-76; goto on_error; } send_pending = 0; } } // Success status = 0; on_error: if (skey) pj_ioqueue_unregister(skey); else if (ssock != -1) pj_sock_close(ssock); if (ckey) pj_ioqueue_unregister(ckey); else if (csock != -1) pj_sock_close(csock); if (ioque != NULL) pj_ioqueue_destroy(ioque); pj_pool_release(pool); return status; }
/* * Create the resolver. */ PJ_DEF(pj_status_t) pj_dns_resolver_create( pj_pool_factory *pf, const char *name, unsigned options, pj_timer_heap_t *timer, pj_ioqueue_t *ioqueue, pj_dns_resolver **p_resolver) { pj_pool_t *pool; pj_dns_resolver *resv; pj_ioqueue_callback socket_cb; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(pf && p_resolver, PJ_EINVAL); if (name == NULL) name = THIS_FILE; /* Create and initialize resolver instance */ pool = pj_pool_create(pf, name, 4000, 4000, NULL); if (!pool) return PJ_ENOMEM; /* Create pool and name */ resv = PJ_POOL_ZALLOC_T(pool, struct pj_dns_resolver); resv->pool = pool; resv->udp_sock = PJ_INVALID_SOCKET; pj_strdup2_with_null(pool, &resv->name, name); /* Create the mutex */ status = pj_mutex_create_recursive(pool, name, &resv->mutex); if (status != PJ_SUCCESS) goto on_error; /* Timer, ioqueue, and settings */ resv->timer = timer; resv->ioqueue = ioqueue; resv->last_id = 1; pj_dns_settings_default(&resv->settings); resv->settings.options = options; /* Create the timer heap if one is not specified */ if (resv->timer == NULL) { status = pj_timer_heap_create(pool, TIMER_SIZE, &resv->timer); if (status != PJ_SUCCESS) goto on_error; } /* Create the ioqueue if one is not specified */ if (resv->ioqueue == NULL) { status = pj_ioqueue_create(pool, MAX_FD, &resv->ioqueue); if (status != PJ_SUCCESS) goto on_error; } /* Response cache hash table */ resv->hrescache = pj_hash_create(pool, RES_HASH_TABLE_SIZE); /* Query hash table and free list. */ resv->hquerybyid = pj_hash_create(pool, Q_HASH_TABLE_SIZE); resv->hquerybyres = pj_hash_create(pool, Q_HASH_TABLE_SIZE); pj_list_init(&resv->query_free_nodes); /* Create the UDP socket */ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &resv->udp_sock); if (status != PJ_SUCCESS) goto on_error; /* Bind to any address/port */ status = pj_sock_bind_in(resv->udp_sock, 0, 0); if (status != PJ_SUCCESS) goto on_error; /* Register to ioqueue */ pj_bzero(&socket_cb, sizeof(socket_cb)); socket_cb.on_read_complete = &on_read_complete; status = pj_ioqueue_register_sock(pool, resv->ioqueue, resv->udp_sock, resv, &socket_cb, &resv->udp_key); if (status != PJ_SUCCESS) goto on_error; pj_ioqueue_op_key_init(&resv->udp_op_key, sizeof(resv->udp_op_key)); /* Start asynchronous read to the UDP socket */ resv->udp_len = sizeof(resv->udp_rx_pkt); resv->udp_addr_len = sizeof(resv->udp_src_addr); status = pj_ioqueue_recvfrom(resv->udp_key, &resv->udp_op_key, resv->udp_rx_pkt, &resv->udp_len, PJ_IOQUEUE_ALWAYS_ASYNC, &resv->udp_src_addr, &resv->udp_addr_len); if (status != PJ_EPENDING) goto on_error; /* Looks like everything is okay */ *p_resolver = resv; return PJ_SUCCESS; on_error: pj_dns_resolver_destroy(resv, PJ_FALSE); return status; }
static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_received) { pj_status_t rc; struct op_key *recv_rec = (struct op_key *)op_key; for (;;) { struct op_key *send_rec = recv_rec->peer; recv_rec->is_pending = 0; if (bytes_received < 0) { if (-bytes_received != recv_rec->last_err) { recv_rec->last_err = (pj_status_t)-bytes_received; app_perror("...error receiving data", recv_rec->last_err); } } else if (bytes_received == 0) { /* note: previous error, or write callback */ } else { pj_atomic_add(total_bytes, (pj_atomic_value_t)bytes_received); if (!send_rec->is_pending) { pj_ssize_t sent = bytes_received; pj_memcpy(send_rec->buffer, recv_rec->buffer, bytes_received); pj_memcpy(&send_rec->addr, &recv_rec->addr, recv_rec->addrlen); send_rec->addrlen = recv_rec->addrlen; rc = pj_ioqueue_sendto(key, &send_rec->op_key_, send_rec->buffer, &sent, 0, &send_rec->addr, send_rec->addrlen); send_rec->is_pending = (rc==PJ_EPENDING); if (rc!=PJ_SUCCESS && rc!=PJ_EPENDING) { app_perror("...send error(1)", rc); } } } if (!send_rec->is_pending) { bytes_received = recv_rec->size; rc = pj_ioqueue_recvfrom(key, &recv_rec->op_key_, recv_rec->buffer, &bytes_received, 0, &recv_rec->addr, &recv_rec->addrlen); recv_rec->is_pending = (rc==PJ_EPENDING); if (rc == PJ_SUCCESS) { /* fall through next loop. */ } else if (rc == PJ_EPENDING) { /* quit callback. */ break; } else { /* error */ app_perror("...recv error", rc); recv_rec->last_err = rc; bytes_received = 0; /* fall through next loop. */ } } else { /* recv will be done when write completion callback is called. */ break; } } }
/* Notification from ioqueue about incoming RTP packet */ static void on_rx_rtp( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { struct transport_udp *udp; pj_status_t status; PJ_UNUSED_ARG(op_key); udp = (struct transport_udp*) pj_ioqueue_get_user_data(key); do { void (*cb)(void*,void*,pj_ssize_t); void *user_data; pj_bool_t discard = PJ_FALSE; cb = udp->rtp_cb; user_data = udp->user_data; /* Simulate packet lost on RX direction */ if (udp->rx_drop_pct) { if ((pj_rand() % 100) <= (int)udp->rx_drop_pct) { PJ_LOG(5,(udp->base.name, "RX RTP packet dropped because of pkt lost " "simulation")); discard = PJ_TRUE; } } /* See if source address of RTP packet is different than the * configured address, and switch RTP remote address to * source packet address after several consecutive packets * have been received. */ if (bytes_read>0 && (udp->options & PJMEDIA_UDP_NO_SRC_ADDR_CHECKING)==0) { if (pj_sockaddr_cmp(&udp->rem_rtp_addr, &udp->rtp_src_addr) == 0) { /* We're still receiving from rem_rtp_addr. Don't switch. */ udp->rtp_src_cnt = 0; } else { udp->rtp_src_cnt++; if (udp->rtp_src_cnt < PJMEDIA_RTP_NAT_PROBATION_CNT) { discard = PJ_TRUE; } else { char addr_text[80]; /* Set remote RTP address to source address */ pj_memcpy(&udp->rem_rtp_addr, &udp->rtp_src_addr, sizeof(pj_sockaddr)); /* Reset counter */ udp->rtp_src_cnt = 0; PJ_LOG(4,(udp->base.name, "Remote RTP address switched to %s", pj_sockaddr_print(&udp->rtp_src_addr, addr_text, sizeof(addr_text), 3))); /* Also update remote RTCP address if actual RTCP source * address is not heard yet. */ if (!pj_sockaddr_has_addr(&udp->rtcp_src_addr)) { pj_uint16_t port; pj_memcpy(&udp->rem_rtcp_addr, &udp->rem_rtp_addr, sizeof(pj_sockaddr)); pj_sockaddr_copy_addr(&udp->rem_rtcp_addr, &udp->rem_rtp_addr); port = (pj_uint16_t) (pj_sockaddr_get_port(&udp->rem_rtp_addr)+1); pj_sockaddr_set_port(&udp->rem_rtcp_addr, port); pj_memcpy(&udp->rtcp_src_addr, &udp->rem_rtcp_addr, sizeof(pj_sockaddr)); PJ_LOG(4,(udp->base.name, "Remote RTCP address switched to predicted" " address %s", pj_sockaddr_print(&udp->rtcp_src_addr, addr_text, sizeof(addr_text), 3))); } } } } if (!discard && udp->attached && cb) (*cb)(user_data, udp->rtp_pkt, bytes_read); bytes_read = sizeof(udp->rtp_pkt); udp->rtp_addrlen = sizeof(udp->rtp_src_addr); status = pj_ioqueue_recvfrom(udp->rtp_key, &udp->rtp_read_op, udp->rtp_pkt, &bytes_read, 0, &udp->rtp_src_addr, &udp->rtp_addrlen); if (status != PJ_EPENDING && status != PJ_SUCCESS) bytes_read = -status; } while (status != PJ_EPENDING && status != PJ_ECANCELLED); }