/* 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); }
/* * 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); }
/* * udp_on_write_complete() * * This is callback notification from ioqueue that a pending sendto() * operation has completed. */ static void udp_on_write_complete( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_sent) { struct udp_transport *tp = (struct udp_transport*) pj_ioqueue_get_user_data(key); pjsip_tx_data_op_key *tdata_op_key = (pjsip_tx_data_op_key*)op_key; tdata_op_key->tdata = NULL; #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 if (-bytes_sent == PJ_ESOCKETSTOP) { pj_status_t status; /* Try to recover by restarting the transport. */ PJ_LOG(4,(tp->base.obj_name, "Restarting SIP UDP transport")); status = pjsip_udp_transport_restart2( &tp->base, PJSIP_UDP_TRANSPORT_DESTROY_SOCKET, PJ_INVALID_SOCKET, &tp->base.local_addr, &tp->base.local_name); if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "Error restarting SIP UDP transport")); } return; } #endif if (tdata_op_key->callback) { tdata_op_key->callback(&tp->base, tdata_op_key->token, bytes_sent); } }
/* Callback when data has been written. * Increment item->bytes_sent and write the next data. */ static void on_write_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_sent) { test_item *item = (test_item*) pj_ioqueue_get_user_data(key); //TRACE_((THIS_FILE, " write complete: sent = %d", bytes_sent)); if (thread_quit_flag) return; item->has_pending_send = 0; item->bytes_sent += bytes_sent; if (bytes_sent <= 0) { PJ_LOG(3,(THIS_FILE, "...error: sending stopped. bytes_sent=%d", bytes_sent)); } else { pj_status_t rc; bytes_sent = item->buffer_size; rc = pj_ioqueue_send( item->client_key, op_key, item->outgoing_buffer, &bytes_sent, 0); if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { app_perror("...error: write error", rc); } item->has_pending_send = (rc==PJ_EPENDING); } }
static void ioqueue_on_connect_complete(pj_ioqueue_key_t *key, pj_status_t status) { pj_activesock_t *asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key); /* Ignore if we've been shutdown */ if (asock->shutdown) return; if (asock->cb.on_connect_complete) { pj_bool_t ret; ret = (*asock->cb.on_connect_complete)(asock, status); if (!ret) { /* We've been destroyed */ return; } #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 activesock_create_iphone_os_stream(asock); #endif } }
/* * Callback on new TCP connection. */ static void lis_on_accept_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t sock, pj_status_t status) { struct tcp_listener *tcp_lis; struct accept_op *accept_op = (struct accept_op*) op_key; tcp_lis = (struct tcp_listener*) pj_ioqueue_get_user_data(key); PJ_UNUSED_ARG(sock); do { /* Report new connection. */ if (status == PJ_SUCCESS) { char addr[PJ_INET6_ADDRSTRLEN+8]; PJ_LOG(5,(tcp_lis->base.obj_name, "Incoming TCP from %s", pj_sockaddr_print(&accept_op->src_addr, addr, sizeof(addr), 3))); transport_create(accept_op->sock, &tcp_lis->base, &accept_op->src_addr, accept_op->src_addr_len); } else if (status != PJ_EPENDING) { show_err(tcp_lis->base.obj_name, "accept()", status); } /* Prepare next accept() */ accept_op->src_addr_len = sizeof(accept_op->src_addr); status = pj_ioqueue_accept(key, op_key, &accept_op->sock, NULL, &accept_op->src_addr, &accept_op->src_addr_len); } while (status != PJ_EPENDING && status != PJ_ECANCELLED && status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)); }
// // Static connect completion callback. // static void connect_complete_cb(pj_ioqueue_key_t *key, pj_status_t status) { Pj_Event_Handler *handler = (Pj_Event_Handler*) pj_ioqueue_get_user_data(key); handler->on_connect_complete(status); }
static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t new_sock, pj_status_t status) { pj_activesock_t *asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key); struct accept_op *accept_op = (struct accept_op*) op_key; PJ_UNUSED_ARG(new_sock); do { if (status == asock->last_err && status != PJ_SUCCESS) { asock->err_counter++; if (asock->err_counter >= PJ_ACTIVESOCK_MAX_CONSECUTIVE_ACCEPT_ERROR) { PJ_LOG(3, ("", "Received %d consecutive errors: %d for the accept()" " operation, stopping further ioqueue accepts.", asock->err_counter, asock->last_err)); return; } } else { asock->err_counter = 0; asock->last_err = status; } if (status==PJ_SUCCESS && asock->cb.on_accept_complete) { pj_bool_t ret; /* Notify callback */ ret = (*asock->cb.on_accept_complete)(asock, accept_op->new_sock, &accept_op->rem_addr, accept_op->rem_addr_len); /* If callback returns false, we have been destroyed! */ if (!ret) return; #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 activesock_create_iphone_os_stream(asock); #endif } else if (status==PJ_SUCCESS) { /* Application doesn't handle the new socket, we need to * close it to avoid resource leak. */ pj_sock_close(accept_op->new_sock); } /* Prepare next accept() */ accept_op->new_sock = PJ_INVALID_SOCKET; accept_op->rem_addr_len = sizeof(accept_op->rem_addr); status = pj_ioqueue_accept(asock->key, op_key, &accept_op->new_sock, NULL, &accept_op->rem_addr, &accept_op->rem_addr_len); } while (status != PJ_EPENDING && status != PJ_ECANCELLED); }
// // Static write completion callback. // static void write_complete_cb(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_sent) { Pj_Event_Handler *handler = (Pj_Event_Handler*) pj_ioqueue_get_user_data(key); handler->on_write_complete((Pj_Async_Op*)op_key, bytes_sent); }
// // Static accept completion callback. // static void accept_complete_cb(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t new_sock, pj_status_t status) { Pj_Event_Handler *handler = (Pj_Event_Handler*) pj_ioqueue_get_user_data(key); handler->on_accept_complete((Pj_Async_Op*)op_key, new_sock, status); }
static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { unsigned *p_packet_cnt = (unsigned*) pj_ioqueue_get_user_data(key); PJ_UNUSED_ARG(op_key); PJ_UNUSED_ARG(bytes_read); (*p_packet_cnt)++; }
/* 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); }
/* * udp_on_write_complete() * * This is callback notification from ioqueue that a pending sendto() * operation has completed. */ static void udp_on_write_complete( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_sent) { struct udp_transport *tp = (struct udp_transport*) pj_ioqueue_get_user_data(key); pjsip_tx_data_op_key *tdata_op_key = (pjsip_tx_data_op_key*)op_key; tdata_op_key->tdata = NULL; if (tdata_op_key->callback) { tdata_op_key->callback(&tp->base, tdata_op_key->token, bytes_sent); } }
static void ioqueue_on_write_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_sent) { pj_activesock_t *asock; asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key); /* Ignore if we've been shutdown. This may cause data to be partially * sent even when 'wholedata' was requested if the OS only sent partial * buffer. */ if (asock->shutdown & SHUT_TX) return; if (bytes_sent > 0 && op_key->activesock_data) { /* whole_data is requested. Make sure we send all the data */ struct send_data *sd = (struct send_data*)op_key->activesock_data; sd->sent += bytes_sent; if (sd->sent == sd->len) { /* all has been sent */ bytes_sent = sd->sent; op_key->activesock_data = NULL; } else { /* send remaining data */ pj_status_t status; status = send_remaining(asock, op_key); if (status == PJ_EPENDING) return; else if (status == PJ_SUCCESS) bytes_sent = sd->sent; else bytes_sent = -status; op_key->activesock_data = NULL; } } if (asock->cb.on_data_sent) { pj_bool_t ret; ret = (*asock->cb.on_data_sent)(asock, op_key, bytes_sent); /* If callback returns false, we have been destroyed! */ if (!ret) return; } }
/* * 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); }
static void ioqueue_on_connect_complete(pj_ioqueue_key_t *key, pj_status_t status) { pj_activesock_t *asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key); if (asock->cb.on_connect_complete) { pj_bool_t ret; ret = (*asock->cb.on_connect_complete)(asock, status); if (!ret) { /* We've been destroyed */ return; } } }
/* * 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)); }
static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t new_sock, pj_status_t status) { pj_activesock_t *asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key); struct accept_op *accept_op = (struct accept_op*) op_key; do { if (status==PJ_SUCCESS && asock->cb.on_accept_complete) { pj_bool_t ret; /* Notify callback */ ret = (*asock->cb.on_accept_complete)(asock, new_sock, &accept_op->rem_addr, accept_op->rem_addr_len); /* If callback returns false, we have been destroyed! */ if (!ret) return; } else if (status==PJ_SUCCESS) { /* Application doesn't handle the new socket, we need to * close it to avoid resource leak. */ pj_sock_close(accept_op->new_sock); } /* Prepare next accept() */ accept_op->new_sock = PJ_INVALID_SOCKET; accept_op->rem_addr_len = sizeof(accept_op->rem_addr); status = pj_ioqueue_accept(asock->key, op_key, &accept_op->new_sock, NULL, &accept_op->rem_addr, &accept_op->rem_addr_len); } while (status != PJ_EPENDING && status != PJ_ECANCELLED); }
/* 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); }
/* Callback when data has been read. * Increment item->bytes_recv and ready to read the next data. */ static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { test_item *item = (test_item*)pj_ioqueue_get_user_data(key); pj_status_t rc; int data_is_available = 1; //TRACE_((THIS_FILE, " read complete, bytes_read=%d", bytes_read)); do { if (thread_quit_flag) return; if (bytes_read < 0) { char errmsg[PJ_ERR_MSG_SIZE]; rc = (pj_status_t)-bytes_read; if (rc != last_error) { //last_error = rc; pj_strerror(rc, errmsg, sizeof(errmsg)); PJ_LOG(3,(THIS_FILE,"...error: read error, bytes_read=%d (%s)", bytes_read, errmsg)); PJ_LOG(3,(THIS_FILE, ".....additional info: total read=%u, total sent=%u", item->bytes_recv, item->bytes_sent)); } else { last_error_counter++; } bytes_read = 0; } else if (bytes_read == 0) { PJ_LOG(3,(THIS_FILE, "...socket has closed!")); } item->bytes_recv += bytes_read; /* To assure that the test quits, even if main thread * doesn't have time to run. */ if (item->bytes_recv > item->buffer_size * 10000) thread_quit_flag = 1; bytes_read = item->buffer_size; rc = pj_ioqueue_recv( key, op_key, item->incoming_buffer, &bytes_read, 0 ); if (rc == PJ_SUCCESS) { data_is_available = 1; } else if (rc == PJ_EPENDING) { data_is_available = 0; } else { data_is_available = 0; if (rc != last_error) { last_error = rc; app_perror("...error: read error(1)", rc); } else { last_error_counter++; } } if (!item->has_pending_send) { pj_ssize_t sent = item->buffer_size; rc = pj_ioqueue_send(item->client_key, &item->send_op, item->outgoing_buffer, &sent, 0); if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { app_perror("...error: write error", rc); } item->has_pending_send = (rc==PJ_EPENDING); } } while (data_is_available); }
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); }
/* 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); }