/***************************************************************************** * test: play WAV file */ static void systest_play_wav(unsigned path_cnt, const char *paths[]) { pjsua_player_id play_id = PJSUA_INVALID_ID; enum gui_key key; test_item_t *ti; const char *title = "WAV File Playback Test"; pj_status_t status; ti = systest_alloc_test_item(title); if (!ti) return; pj_ansi_snprintf(textbuf, sizeof(textbuf), "This test will play %s file to " "the speaker. Please listen carefully for audio " "impairments such as stutter. Let this test run " "for a while to make sure that everything is okay." " Press OK to start, CANCEL to skip", paths[0]); key = gui_msgbox(title, textbuf, WITH_OKCANCEL); if (key != KEY_OK) { ti->skipped = PJ_TRUE; return; } PJ_LOG(3,(THIS_FILE, "Running %s", title)); /* WAV port */ status = create_player(path_cnt, paths, &play_id); if (status != PJ_SUCCESS) goto on_return; status = pjsua_conf_connect(pjsua_player_get_conf_port(play_id), 0); if (status != PJ_SUCCESS) goto on_return; key = gui_msgbox(title, "WAV file should be playing now in the " "speaker. Press OK to stop. ", WITH_OK); status = PJ_SUCCESS; on_return: if (play_id != -1) pjsua_player_destroy(play_id); if (status != PJ_SUCCESS) { systest_perror("Sorry we've encountered error", status); ti->success = PJ_FALSE; pj_strerror(status, ti->reason, sizeof(ti->reason)); } else { key = gui_msgbox(title, "Is the audio okay?", WITH_YESNO); ti->success = (key == KEY_YES); if (!ti->success) pj_ansi_strcpy(ti->reason, USER_ERROR); } return; }
/* * Register new transport type to PJSIP. */ PJ_DEF(pj_status_t) pjsip_transport_register_type( unsigned tp_flag, const char *tp_name, int def_port, int *p_tp_type) { unsigned i; PJ_ASSERT_RETURN(tp_flag && tp_name && def_port, PJ_EINVAL); PJ_ASSERT_RETURN(pj_ansi_strlen(tp_name) < PJ_ARRAY_SIZE(transport_names[0].name_buf), PJ_ENAMETOOLONG); for (i=1; i<PJ_ARRAY_SIZE(transport_names); ++i) { if (transport_names[i].type == 0) break; } if (i == PJ_ARRAY_SIZE(transport_names)) return PJ_ETOOMANY; transport_names[i].type = (pjsip_transport_type_e)i; transport_names[i].port = (pj_uint16_t)def_port; pj_ansi_strcpy(transport_names[i].name_buf, tp_name); transport_names[i].name = pj_str(transport_names[i].name_buf); transport_names[i].flag = tp_flag; if (p_tp_type) *p_tp_type = i; return PJ_SUCCESS; }
/*! * \brief Pass WebSocket data into pjsip transport manager. */ static int transport_read(void *data) { struct transport_read_data *read_data = data; struct ws_transport *newtransport = read_data->transport; struct ast_websocket *session = newtransport->ws_session; pjsip_rx_data *rdata = &newtransport->rdata; int recvd; pj_str_t buf; int pjsip_pkt_len; pj_gettimeofday(&rdata->pkt_info.timestamp); pjsip_pkt_len = PJSIP_MAX_PKT_LEN < read_data->payload_len ? PJSIP_MAX_PKT_LEN : read_data->payload_len; pj_memcpy(rdata->pkt_info.packet, read_data->payload, pjsip_pkt_len); rdata->pkt_info.len = pjsip_pkt_len; rdata->pkt_info.zero = 0; pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ast_sockaddr_stringify(ast_websocket_remote_address(session))), &rdata->pkt_info.src_addr); rdata->pkt_info.src_addr.addr.sa_family = pj_AF_INET(); rdata->pkt_info.src_addr_len = sizeof(rdata->pkt_info.src_addr); pj_ansi_strcpy(rdata->pkt_info.src_name, ast_sockaddr_stringify_host(ast_websocket_remote_address(session))); rdata->pkt_info.src_port = ast_sockaddr_port(ast_websocket_remote_address(session)); recvd = pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, rdata); pj_pool_reset(rdata->tp_info.pool); return (read_data->payload_len == recvd) ? 0 : -1; }
/* * pj_init(void). * Init PJLIB! */ PJ_DEF(pj_status_t) pj_init(void) { char stack_ptr; pj_status_t status; /* Check if PJLIB have been initialized */ if (initialized) { ++initialized; return PJ_SUCCESS; } pj_ansi_strcpy(main_thread.obj_name, "pjthread"); // Init main thread pj_memset(&main_thread, 0, sizeof(main_thread)); // Initialize PjSymbianOS instance PjSymbianOS *os = PjSymbianOS::Instance(); PJ_LOG(4,(THIS_FILE, "Initializing PJLIB for Symbian OS..")); TInt err; err = os->Initialize(); if (err != KErrNone) return PJ_RETURN_OS_ERROR(err); /* Init logging */ pj_log_init(); /* Initialize exception ID for the pool. * Must do so after critical section is configured. */ status = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); if (status != PJ_SUCCESS) goto on_error; #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 main_thread.stk_start = &stack_ptr; main_thread.stk_size = 0xFFFFFFFFUL; main_thread.stk_max_usage = 0; #else stack_ptr = '\0'; #endif /* Flag PJLIB as initialized */ ++initialized; pj_assert(initialized == 1); PJ_LOG(5,(THIS_FILE, "PJLIB initialized.")); return PJ_SUCCESS; on_error: pj_shutdown(); return PJ_RETURN_OS_ERROR(err); }
/* * Create ICE media transport. */ PJ_DEF(pj_status_t) pjmedia_ice_create2(pjmedia_endpt *endpt, const char *name, unsigned comp_cnt, const pj_ice_strans_cfg *cfg, const pjmedia_ice_cb *cb, unsigned options, pjmedia_transport **p_tp) { pj_pool_t *pool; pj_ice_strans_cb ice_st_cb; struct transport_ice *tp_ice; pj_status_t status; PJ_ASSERT_RETURN(endpt && comp_cnt && cfg && p_tp, PJ_EINVAL); /* Create transport instance */ pool = pjmedia_endpt_create_pool(endpt, name, 512, 512); tp_ice = PJ_POOL_ZALLOC_T(pool, struct transport_ice); tp_ice->pool = pool; tp_ice->af = cfg->af; tp_ice->options = options; tp_ice->comp_cnt = comp_cnt; pj_ansi_strcpy(tp_ice->base.name, pool->obj_name); tp_ice->base.op = &transport_ice_op; tp_ice->base.type = PJMEDIA_TRANSPORT_TYPE_ICE; tp_ice->initial_sdp = PJ_TRUE; tp_ice->oa_role = ROLE_NONE; tp_ice->use_ice = PJ_FALSE; if (cb) pj_memcpy(&tp_ice->cb, cb, sizeof(pjmedia_ice_cb)); /* Assign return value first because ICE might call callback * in create() */ *p_tp = &tp_ice->base; /* Configure ICE callbacks */ pj_bzero(&ice_st_cb, sizeof(ice_st_cb)); ice_st_cb.on_ice_complete = &ice_on_ice_complete; ice_st_cb.on_rx_data = &ice_on_rx_data; /* Create ICE */ status = pj_ice_strans_create(name, cfg, comp_cnt, tp_ice, &ice_st_cb, &tp_ice->ice_st); if (status != PJ_SUCCESS) { pj_pool_release(pool); *p_tp = NULL; return status; } /* Done */ return PJ_SUCCESS; }
/* Open pcap file */ PJ_DEF(pj_status_t) pj_pcap_open(pj_pool_t *pool, const char *path, pj_pcap_file **p_file) { pj_pcap_file *file; pj_ssize_t sz; pj_status_t status; PJ_ASSERT_RETURN(pool && path && p_file, PJ_EINVAL); /* More sanity checks */ TRACE_(("pcap", "sizeof(pj_pcap_eth_hdr)=%d", sizeof(pj_pcap_eth_hdr))); PJ_ASSERT_RETURN(sizeof(pj_pcap_eth_hdr)==14, PJ_EBUG); TRACE_(("pcap", "sizeof(pj_pcap_ip_hdr)=%d", sizeof(pj_pcap_ip_hdr))); PJ_ASSERT_RETURN(sizeof(pj_pcap_ip_hdr)==20, PJ_EBUG); TRACE_(("pcap", "sizeof(pj_pcap_udp_hdr)=%d", sizeof(pj_pcap_udp_hdr))); PJ_ASSERT_RETURN(sizeof(pj_pcap_udp_hdr)==8, PJ_EBUG); file = PJ_POOL_ZALLOC_T(pool, pj_pcap_file); pj_ansi_strcpy(file->obj_name, "pcap"); status = pj_file_open(pool, path, PJ_O_RDONLY, &file->fd); if (status != PJ_SUCCESS) return status; /* Read file pcap header */ sz = sizeof(file->hdr); status = pj_file_read(file->fd, &file->hdr, &sz); if (status != PJ_SUCCESS) { pj_file_close(file->fd); return status; } /* Check magic number */ if (file->hdr.magic_number == 0xa1b2c3d4) { file->swap = PJ_FALSE; } else if (file->hdr.magic_number == 0xd4c3b2a1) { file->swap = PJ_TRUE; file->hdr.network = pj_ntohl(file->hdr.network); } else { /* Not PCAP file */ pj_file_close(file->fd); return PJ_EINVALIDOP; } TRACE_((file->obj_name, "PCAP file %s opened", path)); *p_file = file; return PJ_SUCCESS; }
/* Helper function to create "incoming" packet */ struct recv_list *create_incoming_packet( struct loop_transport *loop, pjsip_tx_data *tdata ) { pj_pool_t *pool; struct recv_list *pkt; pool = pjsip_endpt_create_pool(loop->base.endpt, "rdata", PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC+5); if (!pool) return NULL; pkt = pj_pool_zalloc(pool, sizeof(struct recv_list)); /* Initialize rdata. */ pkt->rdata.tp_info.pool = pool; pkt->rdata.tp_info.transport = &loop->base; /* Copy the packet. */ pj_memcpy(pkt->rdata.pkt_info.packet, tdata->buf.start, tdata->buf.cur - tdata->buf.start); pkt->rdata.pkt_info.len = tdata->buf.cur - tdata->buf.start; /* "Source address" info. */ pkt->rdata.pkt_info.src_addr_len = sizeof(pj_sockaddr_in); if (loop->base.key.type == PJSIP_TRANSPORT_LOOP) pj_ansi_strcpy(pkt->rdata.pkt_info.src_name, ADDR_LOOP); else pj_ansi_strcpy(pkt->rdata.pkt_info.src_name, ADDR_LOOP_DGRAM); pkt->rdata.pkt_info.src_port = loop->base.local_name.port; /* When do we need to "deliver" this packet. */ pj_gettimeofday(&pkt->rdata.pkt_info.timestamp); pkt->rdata.pkt_info.timestamp.msec += loop->recv_delay; pj_time_val_normalize(&pkt->rdata.pkt_info.timestamp); /* Done. */ return pkt; }
/**************************************************************************** * configurations */ static void systest_list_audio_devs() { unsigned i, dev_count; pj_size_t len=0; pj_status_t status; test_item_t *ti; enum gui_key key; const char *title = "Audio Device List"; ti = systest_alloc_test_item(title); if (!ti) return; PJ_LOG(3,(THIS_FILE, "Running %s", title)); dev_count = pjmedia_aud_dev_count(); if (dev_count == 0) { key = gui_msgbox(title, "No audio devices are found", WITH_OK); ti->success = PJ_FALSE; pj_ansi_strcpy(ti->reason, "No device found"); return; } pj_ansi_snprintf(ti->reason+len, sizeof(ti->reason)-len, "Found %u devices\r\n", dev_count); len = strlen(ti->reason); for (i=0; i<dev_count; ++i) { pjmedia_aud_dev_info info; status = pjmedia_aud_dev_get_info(i, &info); if (status != PJ_SUCCESS) { systest_perror("Error retrieving device info: ", status); ti->success = PJ_FALSE; pj_strerror(status, ti->reason, sizeof(ti->reason)); return; } pj_ansi_snprintf(ti->reason+len, sizeof(ti->reason)-len, " %2d: %s [%s] (%d/%d)\r\n", i, info.driver, info.name, info.input_count, info.output_count); len = strlen(ti->reason); } ti->reason[len] = '\0'; key = gui_msgbox(title, ti->reason, WITH_OK); PJ_UNUSED_ARG(key); ti->success = PJ_TRUE; }
test_item_t *systest_alloc_test_item(const char *title) { test_item_t *ti; if (test_item_count == SYSTEST_MAX_TEST) { gui_msgbox("Error", "You have done too many tests", WITH_OK); return NULL; } ti = &test_items[test_item_count++]; pj_bzero(ti, sizeof(*ti)); pj_ansi_strcpy(ti->title, title); return ti; }
pjsip_redirect_op on_call_redirected_wrapper (pjsua_call_id call_id, const pjsip_uri *target, const pjsip_event *e) { char uristr[PJSIP_MAX_URL_SIZE]; int len; pj_str_t uri_pstr; len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, target, uristr, sizeof(uristr)); if (len < 1) { pj_ansi_strcpy(uristr, "--URI too long--"); } uri_pstr = pj_str(uristr); return registeredCallbackObject->on_call_redirected(call_id, &uri_pstr); }
/* API: init factory */ static pj_status_t factory_init(pjmedia_aud_dev_factory *f) { struct mda_factory *af = (struct mda_factory*)f; pj_ansi_strcpy(af->dev_info.name, "Symbian Audio"); af->dev_info.default_samples_per_sec = 8000; af->dev_info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING | PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING; af->dev_info.input_count = 1; af->dev_info.output_count = 1; PJ_LOG(4, (THIS_FILE, "Symb Mda initialized")); return PJ_SUCCESS; }
/* * pj_mutex_lock() */ PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) { #if PJ_HAS_THREADS pj_status_t status; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(mutex, PJ_EINVAL); #if PJ_DEBUG PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting (mutex owner=%s)", pj_thread_this()->obj_name, mutex->owner_name)); #else PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting", pj_thread_this()->obj_name)); #endif status = pthread_mutex_lock( &mutex->mutex ); #if PJ_DEBUG if (status == PJ_SUCCESS) { mutex->owner = pj_thread_this(); pj_ansi_strcpy(mutex->owner_name, mutex->owner->obj_name); ++mutex->nesting_level; } PJ_LOG(6,(mutex->obj_name, (status==0 ? "Mutex acquired by thread %s (level=%d)" : "Mutex acquisition FAILED by %s (level=%d)"), pj_thread_this()->obj_name, mutex->nesting_level)); #else PJ_LOG(6,(mutex->obj_name, (status==0 ? "Mutex acquired by thread %s" : "FAILED by %s"), pj_thread_this()->obj_name)); #endif if (status == 0) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(status); #else /* PJ_HAS_THREADS */ pj_assert( mutex == (pj_mutex_t*)1 ); return PJ_SUCCESS; #endif }
/* API: Get device info. */ static pj_status_t android_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_dev_info *info) { PJ_UNUSED_ARG(f); pj_bzero(info, sizeof(*info)); pj_ansi_strcpy(info->name, "Android JNI"); info->default_samples_per_sec = 8000; info->caps = PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING; info->input_count = 1; info->output_count = 1; return PJ_SUCCESS; }
/* * This utility function creates receive data buffers and start * asynchronous recv() operations from the socket. It is called after * accept() or connect() operation complete. */ static pj_status_t tls_start_read(struct tls_transport *tls) { pj_pool_t *pool; pj_ssize_t size; pj_sockaddr_in *rem_addr; void *readbuf[1]; pj_status_t status; /* Init rdata */ pool = pjsip_endpt_create_pool(tls->base.endpt, "rtd%p", PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC); if (!pool) { tls_perror(tls->base.obj_name, "Unable to create pool", PJ_ENOMEM); return PJ_ENOMEM; } tls->rdata.tp_info.pool = pool; tls->rdata.tp_info.transport = &tls->base; tls->rdata.tp_info.tp_data = tls; tls->rdata.tp_info.op_key.rdata = &tls->rdata; pj_ioqueue_op_key_init(&tls->rdata.tp_info.op_key.op_key, sizeof(pj_ioqueue_op_key_t)); tls->rdata.pkt_info.src_addr = tls->base.key.rem_addr; tls->rdata.pkt_info.src_addr_len = sizeof(pj_sockaddr_in); rem_addr = (pj_sockaddr_in*) &tls->base.key.rem_addr; pj_ansi_strcpy(tls->rdata.pkt_info.src_name, pj_inet_ntoa(rem_addr->sin_addr)); tls->rdata.pkt_info.src_port = pj_ntohs(rem_addr->sin_port); size = sizeof(tls->rdata.pkt_info.packet); readbuf[0] = tls->rdata.pkt_info.packet; status = pj_ssl_sock_start_read2(tls->ssock, tls->base.pool, size, readbuf, 0); if (status != PJ_SUCCESS && status != PJ_EPENDING) { PJ_LOG(4, (tls->base.obj_name, "pj_ssl_sock_start_read() error, status=%d", status)); return status; } return PJ_SUCCESS; }
static void pj_perror_imp(int log_level, const char *sender, pj_status_t status, const char *title_fmt, va_list marker) { char titlebuf[PJ_PERROR_TITLE_BUF_SIZE]; char errmsg[PJ_ERR_MSG_SIZE]; int len; /* Build the title */ len = pj_ansi_vsnprintf(titlebuf, sizeof(titlebuf), title_fmt, marker); if (len < 0 || len >= sizeof(titlebuf)) pj_ansi_strcpy(titlebuf, "Error"); /* Get the error */ pj_strerror(status, errmsg, sizeof(errmsg)); /* Send to log */ invoke_log(sender, log_level, "%s: %s", titlebuf, errmsg); }
/* * pj_mutex_trylock() */ PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) { #if PJ_HAS_THREADS int status; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(mutex, PJ_EINVAL); PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is trying", pj_thread_this()->obj_name)); status = pthread_mutex_trylock( &mutex->mutex ); if (status==0) { #if PJ_DEBUG mutex->owner = pj_thread_this(); pj_ansi_strcpy(mutex->owner_name, mutex->owner->obj_name); ++mutex->nesting_level; PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s (level=%d)", pj_thread_this()->obj_name, mutex->nesting_level)); #else PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s", pj_thread_this()->obj_name)); #endif } else { PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s's trylock() failed", pj_thread_this()->obj_name)); } if (status==0) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(status); #else /* PJ_HAS_THREADS */ pj_assert( mutex == (pj_mutex_t*)1); return PJ_SUCCESS; #endif }
/* * Verify that valid SIP url is given. */ static pj_status_t verify_sip_url(const char *c_url) { pjsip_uri *p; pj_pool_t *pool; char *url; int len = (c_url ? pj_ansi_strlen(c_url) : 0); if (!len) return -1; pool = pj_pool_create(&app.cp.factory, "check%p", 1024, 0, NULL); if (!pool) return PJ_ENOMEM; url = pj_pool_alloc(pool, len+1); pj_ansi_strcpy(url, c_url); url[len] = '\0'; p = pjsip_parse_uri(pool, url, len, 0); if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0) p = NULL; pj_pool_release(pool); return p ? 0 : -1; }
/* * This is the public API to create, initialize, register, and start the * TLS listener. */ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt, const pjsip_tls_setting *opt, const pj_sockaddr_in *local, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory) { pj_pool_t *pool; struct tls_listener *listener; pj_ssl_sock_param ssock_param; pj_sockaddr_in *listener_addr; pj_bool_t has_listener; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL); /* Verify that address given in a_name (if any) is valid */ if (a_name && a_name->host.slen) { pj_sockaddr_in tmp; status = pj_sockaddr_in_init(&tmp, &a_name->host, (pj_uint16_t)a_name->port); if (status != PJ_SUCCESS || tmp.sin_addr.s_addr == PJ_INADDR_ANY || tmp.sin_addr.s_addr == PJ_INADDR_NONE) { /* Invalid address */ return PJ_EINVAL; } } pool = pjsip_endpt_create_pool(endpt, "tlslis", POOL_LIS_INIT, POOL_LIS_INC); PJ_ASSERT_RETURN(pool, PJ_ENOMEM); listener = PJ_POOL_ZALLOC_T(pool, struct tls_listener); listener->factory.pool = pool; listener->factory.type = PJSIP_TRANSPORT_TLS; listener->factory.type_name = "tls"; listener->factory.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TLS); pj_ansi_strcpy(listener->factory.obj_name, "tlslis"); if (opt) pjsip_tls_setting_copy(pool, &listener->tls_setting, opt); else pjsip_tls_setting_default(&listener->tls_setting); status = pj_lock_create_recursive_mutex(pool, "tlslis", &listener->factory.lock); if (status != PJ_SUCCESS) goto on_error; if (async_cnt > MAX_ASYNC_CNT) async_cnt = MAX_ASYNC_CNT; /* Build SSL socket param */ pj_ssl_sock_param_default(&ssock_param); ssock_param.cb.on_accept_complete = &on_accept_complete; ssock_param.cb.on_data_read = &on_data_read; ssock_param.cb.on_data_sent = &on_data_sent; ssock_param.async_cnt = async_cnt; ssock_param.ioqueue = pjsip_endpt_get_ioqueue(endpt); ssock_param.require_client_cert = listener->tls_setting.require_client_cert; ssock_param.timeout = listener->tls_setting.timeout; ssock_param.user_data = listener; ssock_param.verify_peer = PJ_FALSE; /* avoid SSL socket closing the socket * due to verification error */ if (ssock_param.send_buffer_size < PJSIP_MAX_PKT_LEN) ssock_param.send_buffer_size = PJSIP_MAX_PKT_LEN; if (ssock_param.read_buffer_size < PJSIP_MAX_PKT_LEN) ssock_param.read_buffer_size = PJSIP_MAX_PKT_LEN; ssock_param.ciphers_num = listener->tls_setting.ciphers_num; ssock_param.ciphers = listener->tls_setting.ciphers; ssock_param.qos_type = listener->tls_setting.qos_type; ssock_param.qos_ignore_error = listener->tls_setting.qos_ignore_error; pj_memcpy(&ssock_param.qos_params, &listener->tls_setting.qos_params, sizeof(ssock_param.qos_params)); has_listener = PJ_FALSE; switch(listener->tls_setting.method) { case PJSIP_TLSV1_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_TLS1; break; case PJSIP_SSLV2_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL2; break; case PJSIP_SSLV3_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL3; break; case PJSIP_SSLV23_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL23; break; default: ssock_param.proto = PJ_SSL_SOCK_PROTO_DEFAULT; break; } /* Create SSL socket */ status = pj_ssl_sock_create(pool, &ssock_param, &listener->ssock); if (status != PJ_SUCCESS) goto on_error; listener_addr = (pj_sockaddr_in*)&listener->factory.local_addr; if (local) { pj_sockaddr_cp((pj_sockaddr_t*)listener_addr, (const pj_sockaddr_t*)local); } else { pj_sockaddr_in_init(listener_addr, NULL, 0); } /* Check if certificate/CA list for SSL socket is set */ if (listener->tls_setting.cert_file.slen || listener->tls_setting.ca_list_file.slen) { status = pj_ssl_cert_load_from_files(pool, &listener->tls_setting.ca_list_file, &listener->tls_setting.cert_file, &listener->tls_setting.privkey_file, &listener->tls_setting.password, &listener->cert); if (status != PJ_SUCCESS) goto on_error; status = pj_ssl_sock_set_certificate(listener->ssock, pool, listener->cert); if (status != PJ_SUCCESS) goto on_error; } /* Start accepting incoming connections. Note that some TLS/SSL backends * may not support for SSL socket server. */ has_listener = PJ_FALSE; status = pj_ssl_sock_start_accept(listener->ssock, pool, (pj_sockaddr_t*)listener_addr, pj_sockaddr_get_len((pj_sockaddr_t*)listener_addr)); if (status == PJ_SUCCESS || status == PJ_EPENDING) { pj_ssl_sock_info info; has_listener = PJ_TRUE; /* Retrieve the bound address */ status = pj_ssl_sock_get_info(listener->ssock, &info); if (status == PJ_SUCCESS) pj_sockaddr_cp(listener_addr, (pj_sockaddr_t*)&info.local_addr); } else if (status != PJ_ENOTSUP) { goto on_error; } /* If published host/IP is specified, then use that address as the * listener advertised address. */ if (a_name && a_name->host.slen) { /* Copy the address */ listener->factory.addr_name = *a_name; pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, &a_name->host); listener->factory.addr_name.port = a_name->port; } else { /* No published address is given, use the bound address */ /* If the address returns 0.0.0.0, use the default * interface address as the transport's address. */ if (listener_addr->sin_addr.s_addr == 0) { pj_sockaddr hostip; status = pj_gethostip(pj_AF_INET(), &hostip); if (status != PJ_SUCCESS) goto on_error; listener_addr->sin_addr.s_addr = hostip.ipv4.sin_addr.s_addr; } /* Save the address name */ sockaddr_to_host_port(listener->factory.pool, &listener->factory.addr_name, listener_addr); } /* If port is zero, get the bound port */ if (listener->factory.addr_name.port == 0) { listener->factory.addr_name.port = pj_ntohs(listener_addr->sin_port); } pj_ansi_snprintf(listener->factory.obj_name, sizeof(listener->factory.obj_name), "tlslis:%d", listener->factory.addr_name.port); /* Register to transport manager */ listener->endpt = endpt; listener->tpmgr = pjsip_endpt_get_tpmgr(endpt); listener->factory.create_transport2 = lis_create_transport; listener->factory.destroy = lis_destroy; listener->is_registered = PJ_TRUE; status = pjsip_tpmgr_register_tpfactory(listener->tpmgr, &listener->factory); if (status != PJ_SUCCESS) { listener->is_registered = PJ_FALSE; goto on_error; } if (has_listener) { PJ_LOG(4,(listener->factory.obj_name, "SIP TLS listener is ready for incoming connections " "at %.*s:%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port)); } else { PJ_LOG(4,(listener->factory.obj_name, "SIP TLS is ready " "(client only)")); } /* Return the pointer to user */ if (p_factory) *p_factory = &listener->factory; return PJ_SUCCESS; on_error: lis_destroy(&listener->factory); return status; }
/* * 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; } } } }
/* * unregister_test() * Check if callback is still called after socket has been unregistered or * closed. */ static int unregister_test(pj_bool_t allow_concur) { enum { RPORT = 50000, SPORT = 50001 }; pj_pool_t *pool; pj_ioqueue_t *ioqueue; pj_sock_t ssock; pj_sock_t rsock; int addrlen; pj_sockaddr_in addr; pj_ioqueue_key_t *key; pj_ioqueue_op_key_t opkey; pj_ioqueue_callback cb; unsigned packet_cnt; char sendbuf[10], recvbuf[10]; pj_ssize_t bytes; pj_time_val timeout; pj_status_t status; pool = pj_pool_create(mem, "test", 4000, 4000, NULL); if (!pool) { app_perror("Unable to create pool", PJ_ENOMEM); return -100; } status = pj_ioqueue_create(pool, 16, &ioqueue); if (status != PJ_SUCCESS) { app_perror("Error creating ioqueue", status); return -110; } // Set concurrency TRACE_("set concurrency..."); status = pj_ioqueue_set_default_concurrency(ioqueue, allow_concur); if (status != PJ_SUCCESS) { return -112; } /* Create sender socket */ status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, SPORT, &ssock); if (status != PJ_SUCCESS) { app_perror("Error initializing socket", status); return -120; } /* Create receiver socket. */ status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, RPORT, &rsock); if (status != PJ_SUCCESS) { app_perror("Error initializing socket", status); return -130; } /* Register rsock to ioqueue. */ pj_bzero(&cb, sizeof(cb)); cb.on_read_complete = &on_read_complete; packet_cnt = 0; status = pj_ioqueue_register_sock(pool, ioqueue, rsock, &packet_cnt, &cb, &key); if (status != PJ_SUCCESS) { app_perror("Error registering to ioqueue", status); return -140; } /* Init operation key. */ pj_ioqueue_op_key_init(&opkey, sizeof(opkey)); /* Start reading. */ bytes = sizeof(recvbuf); status = pj_ioqueue_recv( key, &opkey, recvbuf, &bytes, 0); if (status != PJ_EPENDING) { app_perror("Expecting PJ_EPENDING, but got this", status); return -150; } /* Init destination address. */ addrlen = sizeof(addr); status = pj_sock_getsockname(rsock, &addr, &addrlen); if (status != PJ_SUCCESS) { app_perror("getsockname error", status); return -160; } /* Override address with 127.0.0.1, since getsockname will return * zero in the address field. */ addr.sin_addr = pj_inet_addr2("127.0.0.1"); /* Init buffer to send */ pj_ansi_strcpy(sendbuf, "Hello0123"); /* Send one packet. */ bytes = sizeof(sendbuf); status = pj_sock_sendto(ssock, sendbuf, &bytes, 0, &addr, sizeof(addr)); if (status != PJ_SUCCESS) { app_perror("sendto error", status); return -170; } /* Check if packet is received. */ timeout.sec = 1; timeout.msec = 0; #ifdef PJ_SYMBIAN pj_symbianos_poll(-1, 1000); #else pj_ioqueue_poll(ioqueue, &timeout); #endif if (packet_cnt != 1) { return -180; } /* Just to make sure things are settled.. */ pj_thread_sleep(100); /* Start reading again. */ bytes = sizeof(recvbuf); status = pj_ioqueue_recv( key, &opkey, recvbuf, &bytes, 0); if (status != PJ_EPENDING) { app_perror("Expecting PJ_EPENDING, but got this", status); return -190; } /* Reset packet counter */ packet_cnt = 0; /* Send one packet. */ bytes = sizeof(sendbuf); status = pj_sock_sendto(ssock, sendbuf, &bytes, 0, &addr, sizeof(addr)); if (status != PJ_SUCCESS) { app_perror("sendto error", status); return -200; } /* Now unregister and close socket. */ pj_ioqueue_unregister(key); /* Poll ioqueue. */ #ifdef PJ_SYMBIAN pj_symbianos_poll(-1, 1000); #else timeout.sec = 1; timeout.msec = 0; pj_ioqueue_poll(ioqueue, &timeout); #endif /* Must NOT receive any packets after socket is closed! */ if (packet_cnt > 0) { PJ_LOG(3,(THIS_FILE, "....errror: not expecting to receive packet " "after socket has been closed")); return -210; } /* Success */ pj_sock_close(ssock); pj_ioqueue_destroy(ioqueue); pj_pool_release(pool); return 0; }
/* * This is the public API to create, initialize, register, and start the * TCP listener. */ PJ_DEF(pj_status_t) pjsip_tcp_transport_start3( pjsip_endpoint *endpt, const pjsip_tcp_transport_cfg *cfg, pjsip_tpfactory **p_factory ) { pj_pool_t *pool; pj_sock_t sock = PJ_INVALID_SOCKET; struct tcp_listener *listener; pj_activesock_cfg asock_cfg; pj_activesock_cb listener_cb; pj_sockaddr *listener_addr; int addr_len; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && cfg->async_cnt, PJ_EINVAL); /* Verify that address given in a_name (if any) is valid */ if (cfg->addr_name.host.slen) { pj_sockaddr tmp; status = pj_sockaddr_init(cfg->af, &tmp, &cfg->addr_name.host, (pj_uint16_t)cfg->addr_name.port); if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) || (cfg->af==pj_AF_INET() && tmp.ipv4.sin_addr.s_addr==PJ_INADDR_NONE)) { /* Invalid address */ return PJ_EINVAL; } } pool = pjsip_endpt_create_pool(endpt, "tcplis", POOL_LIS_INIT, POOL_LIS_INC); PJ_ASSERT_RETURN(pool, PJ_ENOMEM); listener = PJ_POOL_ZALLOC_T(pool, struct tcp_listener); listener->factory.pool = pool; listener->factory.type = cfg->af==pj_AF_INET() ? PJSIP_TRANSPORT_TCP : PJSIP_TRANSPORT_TCP6; listener->factory.type_name = (char*) pjsip_transport_get_type_name(listener->factory.type); listener->factory.flag = pjsip_transport_get_flag_from_type(listener->factory.type); listener->qos_type = cfg->qos_type; pj_memcpy(&listener->qos_params, &cfg->qos_params, sizeof(cfg->qos_params)); pj_ansi_strcpy(listener->factory.obj_name, "tcplis"); if (listener->factory.type==PJSIP_TRANSPORT_TCP6) pj_ansi_strcat(listener->factory.obj_name, "6"); status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name, &listener->factory.lock); if (status != PJ_SUCCESS) goto on_error; /* Create socket */ status = pj_sock_socket(cfg->af, pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) goto on_error; /* Apply QoS, if specified */ status = pj_sock_apply_qos2(sock, cfg->qos_type, &cfg->qos_params, 2, listener->factory.obj_name, "SIP TCP listener socket"); /* Bind address may be different than factory.local_addr because * factory.local_addr will be resolved below. */ pj_sockaddr_cp(&listener->bound_addr, &cfg->bind_addr); /* Bind socket */ listener_addr = &listener->factory.local_addr; pj_sockaddr_cp(listener_addr, &cfg->bind_addr); status = pj_sock_bind(sock, listener_addr, pj_sockaddr_get_len(listener_addr)); if (status != PJ_SUCCESS) goto on_error; /* Retrieve the bound address */ addr_len = pj_sockaddr_get_len(listener_addr); status = pj_sock_getsockname(sock, listener_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; /* If published host/IP is specified, then use that address as the * listener advertised address. */ if (cfg->addr_name.host.slen) { /* Copy the address */ listener->factory.addr_name = cfg->addr_name; pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, &cfg->addr_name.host); listener->factory.addr_name.port = cfg->addr_name.port; } else { /* No published address is given, use the bound address */ /* If the address returns 0.0.0.0, use the default * interface address as the transport's address. */ if (!pj_sockaddr_has_addr(listener_addr)) { pj_sockaddr hostip; status = pj_gethostip(listener->bound_addr.addr.sa_family, &hostip); if (status != PJ_SUCCESS) goto on_error; pj_sockaddr_copy_addr(listener_addr, &hostip); } /* Save the address name */ sockaddr_to_host_port(listener->factory.pool, &listener->factory.addr_name, listener_addr); } /* If port is zero, get the bound port */ if (listener->factory.addr_name.port == 0) { listener->factory.addr_name.port = pj_sockaddr_get_port(listener_addr); } pj_ansi_snprintf(listener->factory.obj_name, sizeof(listener->factory.obj_name), "tcplis:%d", listener->factory.addr_name.port); /* Start listening to the address */ status = pj_sock_listen(sock, PJSIP_TCP_TRANSPORT_BACKLOG); if (status != PJ_SUCCESS) goto on_error; /* Create active socket */ pj_activesock_cfg_default(&asock_cfg); if (cfg->async_cnt > MAX_ASYNC_CNT) asock_cfg.async_cnt = MAX_ASYNC_CNT; else asock_cfg.async_cnt = cfg->async_cnt; pj_bzero(&listener_cb, sizeof(listener_cb)); listener_cb.on_accept_complete = &on_accept_complete; status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), &asock_cfg, pjsip_endpt_get_ioqueue(endpt), &listener_cb, listener, &listener->asock); /* Register to transport manager */ listener->endpt = endpt; listener->tpmgr = pjsip_endpt_get_tpmgr(endpt); listener->factory.create_transport = lis_create_transport; listener->factory.destroy = lis_destroy; listener->is_registered = PJ_TRUE; status = pjsip_tpmgr_register_tpfactory(listener->tpmgr, &listener->factory); if (status != PJ_SUCCESS) { listener->is_registered = PJ_FALSE; goto on_error; } /* Start pending accept() operations */ status = pj_activesock_start_accept(listener->asock, pool); if (status != PJ_SUCCESS) goto on_error; PJ_LOG(4,(listener->factory.obj_name, "SIP TCP listener ready for incoming connections at %.*s:%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port)); /* Return the pointer to user */ if (p_factory) *p_factory = &listener->factory; return PJ_SUCCESS; on_error: if (listener->asock==NULL && sock!=PJ_INVALID_SOCKET) pj_sock_close(sock); lis_destroy(&listener->factory); return status; }
/* * Create new allocation. */ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_transport *transport, const pj_sockaddr_t *src_addr, unsigned src_addr_len, const pj_stun_rx_data *rdata, pj_stun_session *srv_sess, pj_turn_allocation **p_alloc) { pj_turn_srv *srv = transport->listener->server; const pj_stun_msg *msg = rdata->msg; pj_pool_t *pool; alloc_request req; pj_turn_allocation *alloc; pj_stun_session_cb sess_cb; char str_tmp[80]; pj_status_t status; /* Parse ALLOCATE request */ status = parse_allocate_req(&req, srv_sess, rdata, src_addr, src_addr_len); if (status != PJ_SUCCESS) return status; pool = pj_pool_create(srv->core.pf, "alloc%p", 1000, 1000, NULL); /* Init allocation structure */ alloc = PJ_POOL_ZALLOC_T(pool, pj_turn_allocation); alloc->pool = pool; alloc->obj_name = pool->obj_name; alloc->relay.tp.sock = PJ_INVALID_SOCKET; alloc->server = transport->listener->server; alloc->bandwidth = req.bandwidth; /* Set transport */ alloc->transport = transport; pj_turn_transport_add_ref(transport, alloc); alloc->hkey.tp_type = transport->listener->tp_type; pj_memcpy(&alloc->hkey.clt_addr, src_addr, src_addr_len); status = pj_lock_create_recursive_mutex(pool, alloc->obj_name, &alloc->lock); if (status != PJ_SUCCESS) { goto on_error; } /* Create peer hash table */ alloc->peer_table = pj_hash_create(pool, PEER_TABLE_SIZE); /* Create channel hash table */ alloc->ch_table = pj_hash_create(pool, PEER_TABLE_SIZE); /* Print info */ pj_ansi_strcpy(alloc->info, pj_turn_tp_type_name(transport->listener->tp_type)); alloc->info[3] = ':'; pj_sockaddr_print(src_addr, alloc->info+4, sizeof(alloc->info)-4, 3); /* Create STUN session to handle STUN communication with client */ pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_send_msg = &stun_on_send_msg; sess_cb.on_rx_request = &stun_on_rx_request; sess_cb.on_rx_indication = &stun_on_rx_indication; status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name, &sess_cb, PJ_FALSE, NULL, &alloc->sess); if (status != PJ_SUCCESS) { goto on_error; } /* Attach to STUN session */ pj_stun_session_set_user_data(alloc->sess, alloc); /* Init authentication credential */ status = init_cred(alloc, msg); if (status != PJ_SUCCESS) { goto on_error; } /* Attach authentication credential to STUN session */ pj_stun_session_set_credential(alloc->sess, PJ_STUN_AUTH_LONG_TERM, &alloc->cred); /* Create the relay resource */ status = create_relay(srv, alloc, msg, &req, &alloc->relay); if (status != PJ_SUCCESS) { goto on_error; } /* Register this allocation */ pj_turn_srv_register_allocation(srv, alloc); /* Respond to ALLOCATE request */ status = send_allocate_response(alloc, srv_sess, transport, rdata); if (status != PJ_SUCCESS) goto on_error; /* Done */ pj_sockaddr_print(&alloc->relay.hkey.addr, str_tmp, sizeof(str_tmp), 3); PJ_LOG(4,(alloc->obj_name, "Client %s created, relay addr=%s:%s", alloc->info, pj_turn_tp_type_name(req.tp_type), str_tmp)); /* Success */ *p_alloc = alloc; return PJ_SUCCESS; on_error: /* Send reply to the ALLOCATE request */ pj_strerror(status, str_tmp, sizeof(str_tmp)); pj_stun_session_respond(srv_sess, rdata, PJ_STUN_SC_BAD_REQUEST, str_tmp, transport, PJ_TRUE, src_addr, src_addr_len); /* Cleanup */ destroy_allocation(alloc); return status; }
/* * Create a new listener on the specified port. */ PJ_DEF(pj_status_t) pj_turn_listener_create_udp( pj_turn_srv *srv, int af, const pj_str_t *bound_addr, unsigned port, unsigned concurrency_cnt, unsigned flags, pj_turn_listener **p_listener) { pj_pool_t *pool; struct udp_listener *udp; pj_ioqueue_callback ioqueue_cb; unsigned i; pj_status_t status; /* Create structure */ pool = pj_pool_create(srv->core.pf, "udp%p", 1000, 1000, NULL); udp = PJ_POOL_ZALLOC_T(pool, struct udp_listener); udp->base.pool = pool; udp->base.obj_name = pool->obj_name; udp->base.server = srv; udp->base.tp_type = PJ_TURN_TP_UDP; udp->base.sock = PJ_INVALID_SOCKET; udp->base.destroy = &udp_destroy; udp->read_cnt = concurrency_cnt; udp->base.flags = flags; udp->tp.obj_name = udp->base.obj_name; udp->tp.info = udp->base.info; udp->tp.listener = &udp->base; udp->tp.sendto = &udp_sendto; udp->tp.add_ref = &udp_add_ref; udp->tp.dec_ref = &udp_dec_ref; /* Create socket */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &udp->base.sock); if (status != PJ_SUCCESS) goto on_error; /* Init bind address */ status = pj_sockaddr_init(af, &udp->base.addr, bound_addr, (pj_uint16_t)port); if (status != PJ_SUCCESS) goto on_error; /* Create info */ pj_ansi_strcpy(udp->base.info, "UDP:"); pj_sockaddr_print(&udp->base.addr, udp->base.info+4, sizeof(udp->base.info)-4, 3); /* Bind socket */ status = pj_sock_bind(udp->base.sock, &udp->base.addr, pj_sockaddr_get_len(&udp->base.addr)); if (status != PJ_SUCCESS) goto on_error; /* Register to ioqueue */ pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb)); ioqueue_cb.on_read_complete = on_read_complete; status = pj_ioqueue_register_sock(pool, srv->core.ioqueue, udp->base.sock, udp, &ioqueue_cb, &udp->key); /* Create op keys */ udp->read_op = (struct read_op**)pj_pool_calloc(pool, concurrency_cnt, sizeof(struct read_op*)); /* Create each read_op and kick off read operation */ for (i=0; i<concurrency_cnt; ++i) { pj_pool_t *rpool = pj_pool_create(srv->core.pf, "rop%p", 1000, 1000, NULL); udp->read_op[i] = PJ_POOL_ZALLOC_T(pool, struct read_op); udp->read_op[i]->pkt.pool = rpool; on_read_complete(udp->key, &udp->read_op[i]->op_key, 0); } /* Done */ PJ_LOG(4,(udp->base.obj_name, "Listener %s created", udp->base.info)); *p_listener = &udp->base; return PJ_SUCCESS; on_error: udp_destroy(&udp->base); return status; }
static void systest_aec_test(void) { const char *ref_wav_paths[] = { add_path(res_path, WAV_PLAYBACK_PATH), ALT_PATH1 WAV_PLAYBACK_PATH }; pjsua_player_id player_id = PJSUA_INVALID_ID; pjsua_recorder_id writer_id = PJSUA_INVALID_ID; enum gui_key key; test_item_t *ti; const char *title = "AEC/AES Test"; unsigned last_ec_tail = 0; pj_status_t status; pj_str_t tmp; ti = systest_alloc_test_item(title); if (!ti) return; key = gui_msgbox(title, "This test will try to find whether the AEC/AES " "works good on this system. Test will play a file " "while recording from mic. The recording will be " "played back later so you can check if echo is there. " "Press OK to start.", WITH_OKCANCEL); if (key != KEY_OK) { ti->skipped = PJ_TRUE; return; } /* Save current EC tail */ status = pjsua_get_ec_tail(&last_ec_tail); if (status != PJ_SUCCESS) goto on_return; /* Set EC tail setting to default */ status = pjsua_set_ec(PJSUA_DEFAULT_EC_TAIL_LEN, 0); if (status != PJ_SUCCESS) goto on_return; /* * Create player and recorder */ status = create_player(PJ_ARRAY_SIZE(ref_wav_paths), ref_wav_paths, &player_id); if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "Error opening WAV file %s", WAV_PLAYBACK_PATH)); goto on_return; } status = pjsua_recorder_create( pj_cstr(&tmp, add_path(doc_path, AEC_REC_PATH)), 0, 0, -1, 0, &writer_id); if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "Error writing WAV file %s", AEC_REC_PATH)); goto on_return; } /* * Start playback and recording. */ pjsua_conf_connect(pjsua_player_get_conf_port(player_id), 0); pj_thread_sleep(100); pjsua_conf_connect(0, pjsua_recorder_get_conf_port(writer_id)); /* Wait user signal */ gui_msgbox(title, "AEC/AES test is running. Press OK to stop this test.", WITH_OK); /* * Stop and close playback and recorder */ pjsua_conf_disconnect(0, pjsua_recorder_get_conf_port(writer_id)); pjsua_conf_disconnect(pjsua_player_get_conf_port(player_id), 0); pjsua_recorder_destroy(writer_id); pjsua_player_destroy(player_id); player_id = PJSUA_INVALID_ID; writer_id = PJSUA_INVALID_ID; /* * Play the result. */ status = pjsua_player_create( pj_cstr(&tmp, add_path(doc_path, AEC_REC_PATH)), 0, &player_id); if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "Error opening WAV file %s", AEC_REC_PATH)); goto on_return; } pjsua_conf_connect(pjsua_player_get_conf_port(player_id), 0); /* Wait user signal */ gui_msgbox(title, "We are now playing the captured audio from the mic. " "Check if echo (of the audio played back previously) is " "present in the audio. The recording is stored in " AEC_REC_PATH " for offline analysis. " "Press OK to stop.", WITH_OK); pjsua_conf_disconnect(pjsua_player_get_conf_port(player_id), 0); key = gui_msgbox(title, "Did you notice any echo in the recording?", WITH_YESNO); on_return: if (player_id != PJSUA_INVALID_ID) pjsua_player_destroy(player_id); if (writer_id != PJSUA_INVALID_ID) pjsua_recorder_destroy(writer_id); /* Wait until sound device closed before restoring back EC tail setting */ while (pjsua_snd_is_active()) pj_thread_sleep(10); pjsua_set_ec(last_ec_tail, 0); if (status != PJ_SUCCESS) { systest_perror("Sorry we encountered an error: ", status); ti->success = PJ_FALSE; pj_strerror(status, ti->reason, sizeof(ti->reason)); } else if (key == KEY_YES) { ti->success = PJ_FALSE; if (!ti->success) { pj_ansi_strcpy(ti->reason, USER_ERROR); } } else { char msg[200]; pj_ansi_snprintf(msg, sizeof(msg), "Test succeeded.\r\n"); ti->success = PJ_TRUE; pj_ansi_strncpy(ti->reason, msg, sizeof(ti->reason)); ti->reason[sizeof(ti->reason)-1] = '\0'; } }
static void systest_latency_test(void) { const char *ref_wav_paths[] = { add_path(res_path, WAV_TOCK8_PATH), ALT_PATH1 WAV_TOCK8_PATH }; pj_str_t rec_wav_file; pjsua_player_id play_id = PJSUA_INVALID_ID; pjsua_conf_port_id play_slot = PJSUA_INVALID_ID; pjsua_recorder_id rec_id = PJSUA_INVALID_ID; pjsua_conf_port_id rec_slot = PJSUA_INVALID_ID; pj_pool_t *pool = NULL; pjmedia_port *wav_port = NULL; unsigned lat_sum=0, lat_cnt=0, lat_min=0, lat_max=0; enum gui_key key; test_item_t *ti; const char *title = "Audio Latency Test"; pj_status_t status; ti = systest_alloc_test_item(title); if (!ti) return; key = gui_msgbox(title, "This test will try to find the audio device's " "latency. We will play a special WAV file to the " "speaker for ten seconds, then at the end " "calculate the latency. Please don't do anything " "until the test is done.", WITH_OKCANCEL); if (key != KEY_OK) { ti->skipped = PJ_TRUE; return; } key = gui_msgbox(title, "For this test to work, we must be able to capture " "the audio played in the speaker (the echo), and only" " that audio (i.e. you must be in relatively quiet " "place to run this test). " "Press OK to start, or CANCEL to skip.", WITH_OKCANCEL); if (key != KEY_OK) { ti->skipped = PJ_TRUE; return; } PJ_LOG(3,(THIS_FILE, "Running %s", title)); status = create_player(PJ_ARRAY_SIZE(ref_wav_paths), ref_wav_paths, &play_id); if (status != PJ_SUCCESS) goto on_return; play_slot = pjsua_player_get_conf_port(play_id); rec_wav_file = pj_str(add_path(doc_path, WAV_LATENCY_OUT_PATH)); status = pjsua_recorder_create(&rec_wav_file, 0, NULL, -1, 0, &rec_id); if (status != PJ_SUCCESS) goto on_return; rec_slot = pjsua_recorder_get_conf_port(rec_id); /* Setup the test */ //status = pjsua_conf_connect(0, 0); status = pjsua_conf_connect(play_slot, 0); status = pjsua_conf_connect(0, rec_slot); status = pjsua_conf_connect(play_slot, rec_slot); /* We're running */ PJ_LOG(3,(THIS_FILE, "Please wait while test is running (~10 sec)")); gui_sleep(10); /* Done with the test */ //status = pjsua_conf_disconnect(0, 0); status = pjsua_conf_disconnect(play_slot, rec_slot); status = pjsua_conf_disconnect(0, rec_slot); status = pjsua_conf_disconnect(play_slot, 0); pjsua_recorder_destroy(rec_id); rec_id = PJSUA_INVALID_ID; pjsua_player_destroy(play_id); play_id = PJSUA_INVALID_ID; /* Confirm that echo is heard */ gui_msgbox(title, "Test is done. Now we need to confirm that we indeed " "captured the echo. We will play the captured audio " "and please confirm that you can hear the 'tock' echo.", WITH_OK); status = pjsua_player_create(&rec_wav_file, 0, &play_id); if (status != PJ_SUCCESS) goto on_return; play_slot = pjsua_player_get_conf_port(play_id); status = pjsua_conf_connect(play_slot, 0); if (status != PJ_SUCCESS) goto on_return; key = gui_msgbox(title, "The captured audio is being played back now. " "Can you hear the 'tock' echo?", WITH_YESNO); pjsua_player_destroy(play_id); play_id = PJSUA_INVALID_ID; if (key != KEY_YES) goto on_return; /* Now analyze the latency */ pool = pjsua_pool_create("latency", 512, 512); status = pjmedia_wav_player_port_create(pool, rec_wav_file.ptr, 0, 0, 0, &wav_port); if (status != PJ_SUCCESS) goto on_return; status = calculate_latency(pool, wav_port, &lat_sum, &lat_cnt, &lat_min, &lat_max); if (status != PJ_SUCCESS) goto on_return; on_return: if (wav_port) pjmedia_port_destroy(wav_port); if (pool) pj_pool_release(pool); if (play_id != PJSUA_INVALID_ID) pjsua_player_destroy(play_id); if (rec_id != PJSUA_INVALID_ID) pjsua_recorder_destroy(rec_id); if (status != PJ_SUCCESS) { systest_perror("Sorry we encountered an error: ", status); ti->success = PJ_FALSE; pj_strerror(status, ti->reason, sizeof(ti->reason)); } else if (key != KEY_YES) { ti->success = PJ_FALSE; if (!ti->success) { pj_ansi_strcpy(ti->reason, USER_ERROR); } } else { char msg[200]; pj_size_t msglen; pj_ansi_snprintf(msg, sizeof(msg), "The sound device latency:\r\n" " Min=%u, Max=%u, Avg=%u\r\n", lat_min, lat_max, lat_sum/lat_cnt); msglen = strlen(msg); if (lat_sum/lat_cnt > 500) { pj_ansi_snprintf(msg+msglen, sizeof(msg)-msglen, "The latency is huge!\r\n"); msglen = strlen(msg); } else if (lat_sum/lat_cnt > 200) { pj_ansi_snprintf(msg+msglen, sizeof(msg)-msglen, "The latency is quite high\r\n"); msglen = strlen(msg); } key = gui_msgbox(title, msg, WITH_OK); ti->success = PJ_TRUE; pj_ansi_strncpy(ti->reason, msg, sizeof(ti->reason)); ti->reason[sizeof(ti->reason)-1] = '\0'; } }
/***************************************************************************** * test: record audio */ static void systest_rec_audio(void) { const pj_str_t filename = pj_str(add_path(doc_path, WAV_REC_OUT_PATH)); pj_pool_t *pool = NULL; enum gui_key key; pjsua_recorder_id rec_id = PJSUA_INVALID_ID; pjsua_player_id play_id = PJSUA_INVALID_ID; pjsua_conf_port_id rec_slot = PJSUA_INVALID_ID; pjsua_conf_port_id play_slot = PJSUA_INVALID_ID; pj_status_t status = PJ_SUCCESS; const char *title = "Audio Recording"; test_item_t *ti; ti = systest_alloc_test_item(title); if (!ti) return; key = gui_msgbox(title, "This test will allow you to record audio " "from the microphone, and playback the " "audio to the speaker. Press OK to start recording, " "CANCEL to skip.", WITH_OKCANCEL); if (key != KEY_OK) { ti->skipped = PJ_TRUE; return; } PJ_LOG(3,(THIS_FILE, "Running %s", title)); pool = pjsua_pool_create("rectest", 512, 512); status = pjsua_recorder_create(&filename, 0, NULL, -1, 0, &rec_id); if (status != PJ_SUCCESS) goto on_return; rec_slot = pjsua_recorder_get_conf_port(rec_id); status = pjsua_conf_connect(0, rec_slot); if (status != PJ_SUCCESS) goto on_return; key = gui_msgbox(title, "Recording is in progress now, please say " "something in the microphone. Press OK " "to stop recording", WITH_OK); pjsua_conf_disconnect(0, rec_slot); rec_slot = PJSUA_INVALID_ID; pjsua_recorder_destroy(rec_id); rec_id = PJSUA_INVALID_ID; status = pjsua_player_create(&filename, 0, &play_id); if (status != PJ_SUCCESS) goto on_return; play_slot = pjsua_player_get_conf_port(play_id); status = pjsua_conf_connect(play_slot, 0); if (status != PJ_SUCCESS) goto on_return; key = gui_msgbox(title, "Recording has been stopped. " "The recorded audio is being played now to " "the speaker device, in a loop. Listen for " "any audio impairments. Press OK to stop.", WITH_OK); on_return: if (rec_slot != PJSUA_INVALID_ID) pjsua_conf_disconnect(0, rec_slot); if (rec_id != PJSUA_INVALID_ID) pjsua_recorder_destroy(rec_id); if (play_slot != PJSUA_INVALID_ID) pjsua_conf_disconnect(play_slot, 0); if (play_id != PJSUA_INVALID_ID) pjsua_player_destroy(play_id); if (pool) pj_pool_release(pool); if (status != PJ_SUCCESS) { systest_perror("Sorry we encountered an error: ", status); ti->success = PJ_FALSE; pj_strerror(status, ti->reason, sizeof(ti->reason)); } else { key = gui_msgbox(title, "Is the audio okay?", WITH_YESNO); ti->success = (key == KEY_YES); if (!ti->success) { pj_ansi_snprintf(textbuf, sizeof(textbuf), "You will probably need to copy the recorded " "WAV file %s to a desktop computer and analyze " "it, to find out whether it's a recording " "or playback problem.", WAV_REC_OUT_PATH); gui_msgbox(title, textbuf, WITH_OK); pj_ansi_strcpy(ti->reason, USER_ERROR); } } }
/** * 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; }
/* Resolve IPv4/IPv6 address */ PJ_DEF(pj_status_t) pj_getaddrinfo(int af, const pj_str_t *nodename, unsigned *count, pj_addrinfo ai[]) { #if defined(PJ_SOCK_HAS_GETADDRINFO) && PJ_SOCK_HAS_GETADDRINFO!=0 char nodecopy[PJ_MAX_HOSTNAME]; struct addrinfo hint, *res, *orig_res; unsigned i; int rc; PJ_ASSERT_RETURN(nodename && count && *count && ai, PJ_EINVAL); PJ_ASSERT_RETURN(nodename->ptr && nodename->slen, PJ_EINVAL); PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6 || af==PJ_AF_UNSPEC, PJ_EINVAL); /* Check if nodename is IP address */ pj_bzero(&ai[0], sizeof(ai[0])); if (af == PJ_AF_UNSPEC) { if (pj_inet_pton(PJ_AF_INET, nodename, &ai[0].ai_addr.ipv4.sin_addr) == PJ_SUCCESS) { af = PJ_AF_INET; } else if (pj_inet_pton(PJ_AF_INET6, nodename, &ai[0].ai_addr.ipv6.sin6_addr) == PJ_SUCCESS) { af = PJ_AF_INET6; } if (af != PJ_AF_UNSPEC) { pj_str_t tmp; tmp.ptr = ai[0].ai_canonname; pj_strncpy_with_null(&tmp, nodename, PJ_MAX_HOSTNAME); ai[0].ai_addr.addr.sa_family = (pj_uint16_t)af; *count = 1; return PJ_SUCCESS; } } /* Copy node name to null terminated string. */ if (nodename->slen >= PJ_MAX_HOSTNAME) return PJ_ENAMETOOLONG; pj_memcpy(nodecopy, nodename->ptr, nodename->slen); nodecopy[nodename->slen] = '\0'; /* Call getaddrinfo() */ pj_bzero(&hint, sizeof(hint)); hint.ai_family = af; rc = getaddrinfo(nodecopy, NULL, &hint, &res); if (rc != 0) return PJ_ERESOLVE; orig_res = res; /* Enumerate each item in the result */ for (i=0; i<*count && res; res=res->ai_next) { /* Ignore unwanted address families */ if (af!=PJ_AF_UNSPEC && res->ai_family != af) continue; /* Store canonical name (possibly truncating the name) */ if (res->ai_canonname) { pj_ansi_strncpy(ai[i].ai_canonname, res->ai_canonname, sizeof(ai[i].ai_canonname)); ai[i].ai_canonname[sizeof(ai[i].ai_canonname)-1] = '\0'; } else { pj_ansi_strcpy(ai[i].ai_canonname, nodecopy); } /* Store address */ PJ_ASSERT_ON_FAIL(res->ai_addrlen <= sizeof(pj_sockaddr), continue); pj_memcpy(&ai[i].ai_addr, res->ai_addr, res->ai_addrlen); PJ_SOCKADDR_RESET_LEN(&ai[i].ai_addr); /* Next slot */ ++i; } *count = i; freeaddrinfo(orig_res); /* Done */ return PJ_SUCCESS; #else /* PJ_SOCK_HAS_GETADDRINFO */ PJ_ASSERT_RETURN(count && *count, PJ_EINVAL); /* Check if nodename is IP address */ pj_bzero(&ai[0], sizeof(ai[0])); if (af == PJ_AF_UNSPEC) { if (pj_inet_pton(PJ_AF_INET, nodename, &ai[0].ai_addr.ipv4.sin_addr) == PJ_SUCCESS) { af = PJ_AF_INET; } else if (pj_inet_pton(PJ_AF_INET6, nodename, &ai[0].ai_addr.ipv6.sin6_addr) == PJ_SUCCESS) { af = PJ_AF_INET6; } if (af != PJ_AF_UNSPEC) { pj_str_t tmp; tmp.ptr = ai[0].ai_canonname; pj_strncpy_with_null(&tmp, nodename, PJ_MAX_HOSTNAME); ai[0].ai_addr.addr.sa_family = (pj_uint16_t)af; *count = 1; return PJ_SUCCESS; } } if (af == PJ_AF_INET || af == PJ_AF_UNSPEC) { pj_hostent he; unsigned i, max_count; pj_status_t status; /* VC6 complains that "he" is uninitialized */ #ifdef _MSC_VER pj_bzero(&he, sizeof(he)); #endif status = pj_gethostbyname(nodename, &he); if (status != PJ_SUCCESS) return status; max_count = *count; *count = 0; pj_bzero(ai, max_count * sizeof(pj_addrinfo)); for (i=0; he.h_addr_list[i] && *count<max_count; ++i) { pj_ansi_strncpy(ai[*count].ai_canonname, he.h_name, sizeof(ai[*count].ai_canonname)); ai[*count].ai_canonname[sizeof(ai[*count].ai_canonname)-1] = '\0'; ai[*count].ai_addr.ipv4.sin_family = PJ_AF_INET; pj_memcpy(&ai[*count].ai_addr.ipv4.sin_addr, he.h_addr_list[i], he.h_length); PJ_SOCKADDR_RESET_LEN(&ai[*count].ai_addr); (*count)++; } return PJ_SUCCESS; } else { /* IPv6 is not supported */ *count = 0; return PJ_EIPV6NOTSUP; } #endif /* PJ_SOCK_HAS_GETADDRINFO */ }
int transport_rt_test( pjsip_transport_type_e tp_type, pjsip_transport *ref_tp, char *target_url, int *lost) { enum { THREADS = 4, INTERVAL = 10 }; int i; pj_status_t status; pj_pool_t *pool; pj_bool_t logger_enabled; pj_timestamp zero_time, total_time; unsigned usec_rt; unsigned total_sent; unsigned total_recv; PJ_UNUSED_ARG(tp_type); PJ_UNUSED_ARG(ref_tp); PJ_LOG(3,(THIS_FILE, " multithreaded round-trip test (%d threads)...", THREADS)); PJ_LOG(3,(THIS_FILE, " this will take approx %d seconds, please wait..", INTERVAL)); /* Make sure msg logger is disabled. */ logger_enabled = msg_logger_set_enabled(0); /* Register module (if not yet registered) */ if (rt_module.id == -1) { status = pjsip_endpt_register_module( endpt, &rt_module ); if (status != PJ_SUCCESS) { app_perror(" error: unable to register module", status); return -600; } } /* Create pool for this test. */ pool = pjsip_endpt_create_pool(endpt, NULL, 4000, 4000); if (!pool) return -610; /* Initialize static test data. */ pj_ansi_strcpy(rt_target_uri, target_url); rt_call_id = pj_str("RT-Call-Id/"); rt_stop = PJ_FALSE; /* Initialize thread data. */ for (i=0; i<THREADS; ++i) { char buf[1]; pj_str_t str_id; pj_strset(&str_id, buf, 1); pj_bzero(&rt_test_data[i], sizeof(rt_test_data[i])); /* Init timer entry */ rt_test_data[i].tx_timer.id = i; rt_test_data[i].tx_timer.cb = &rt_tx_timer; rt_test_data[i].timeout_timer.id = i; rt_test_data[i].timeout_timer.cb = &rt_timeout_timer; /* Generate Call-ID for each thread. */ rt_test_data[i].call_id.ptr = (char*) pj_pool_alloc(pool, rt_call_id.slen+1); pj_strcpy(&rt_test_data[i].call_id, &rt_call_id); buf[0] = '0' + (char)i; pj_strcat(&rt_test_data[i].call_id, &str_id); /* Init mutex. */ status = pj_mutex_create_recursive(pool, "rt", &rt_test_data[i].mutex); if (status != PJ_SUCCESS) { app_perror(" error: unable to create mutex", status); return -615; } /* Create thread, suspended. */ status = pj_thread_create(pool, "rttest%p", &rt_worker_thread, (void*)(long)i, 0, PJ_THREAD_SUSPENDED, &rt_test_data[i].thread); if (status != PJ_SUCCESS) { app_perror(" error: unable to create thread", status); return -620; } } /* Start threads! */ for (i=0; i<THREADS; ++i) { pj_time_val delay = {0,0}; pj_thread_resume(rt_test_data[i].thread); /* Schedule first message transmissions. */ rt_test_data[i].tx_timer.user_data = (void*)1; pjsip_endpt_schedule_timer(endpt, &rt_test_data[i].tx_timer, &delay); } /* Sleep for some time. */ pj_thread_sleep(INTERVAL * 1000); /* Signal thread to stop. */ rt_stop = PJ_TRUE; /* Wait threads to complete. */ for (i=0; i<THREADS; ++i) { pj_thread_join(rt_test_data[i].thread); pj_thread_destroy(rt_test_data[i].thread); } /* Destroy rt_test_data */ for (i=0; i<THREADS; ++i) { pj_mutex_destroy(rt_test_data[i].mutex); pjsip_endpt_cancel_timer(endpt, &rt_test_data[i].timeout_timer); } /* Gather statistics. */ pj_bzero(&total_time, sizeof(total_time)); pj_bzero(&zero_time, sizeof(zero_time)); usec_rt = total_sent = total_recv = 0; for (i=0; i<THREADS; ++i) { total_sent += rt_test_data[i].sent_request_count; total_recv += rt_test_data[i].recv_response_count; pj_add_timestamp(&total_time, &rt_test_data[i].total_rt_time); } /* Display statistics. */ if (total_recv) total_time.u64 = total_time.u64/total_recv; else total_time.u64 = 0; usec_rt = pj_elapsed_usec(&zero_time, &total_time); PJ_LOG(3,(THIS_FILE, " done.")); PJ_LOG(3,(THIS_FILE, " total %d messages sent", total_sent)); PJ_LOG(3,(THIS_FILE, " average round-trip=%d usec", usec_rt)); pjsip_endpt_release_pool(endpt, pool); *lost = total_sent-total_recv; /* Flush events. */ flush_events(500); /* Restore msg logger. */ msg_logger_set_enabled(logger_enabled); return 0; }
/***************************************************************************** * test: play simple ringback tone and hear it */ static void systest_play_tone(void) { /* Ringtones */ #define RINGBACK_FREQ1 440 /* 400 */ #define RINGBACK_FREQ2 480 /* 450 */ #define RINGBACK_ON 3000 /* 400 */ #define RINGBACK_OFF 4000 /* 200 */ #define RINGBACK_CNT 1 /* 2 */ #define RINGBACK_INTERVAL 4000 /* 2000 */ unsigned i, samples_per_frame; pjmedia_tone_desc tone[RINGBACK_CNT]; pj_pool_t *pool = NULL; pjmedia_port *ringback_port = NULL; enum gui_key key; int ringback_slot = -1; test_item_t *ti; pj_str_t name; const char *title = "Audio Tone Playback Test"; pj_status_t status; ti = systest_alloc_test_item(title); if (!ti) return; key = gui_msgbox(title, "This test will play simple ringback tone to " "the speaker. Please listen carefully for audio " "impairments such as stutter. You may need " "to let this test running for a while to " "make sure that everything is okay. Press " "OK to start, CANCEL to skip", WITH_OKCANCEL); if (key != KEY_OK) { ti->skipped = PJ_TRUE; return; } PJ_LOG(3,(THIS_FILE, "Running %s", title)); pool = pjsua_pool_create("ringback", 512, 512); samples_per_frame = systest.media_cfg.audio_frame_ptime * systest.media_cfg.clock_rate * systest.media_cfg.channel_count / 1000; /* Ringback tone (call is ringing) */ name = pj_str("ringback"); status = pjmedia_tonegen_create2(pool, &name, systest.media_cfg.clock_rate, systest.media_cfg.channel_count, samples_per_frame, 16, PJMEDIA_TONEGEN_LOOP, &ringback_port); if (status != PJ_SUCCESS) goto on_return; pj_bzero(&tone, sizeof(tone)); for (i=0; i<RINGBACK_CNT; ++i) { tone[i].freq1 = RINGBACK_FREQ1; tone[i].freq2 = RINGBACK_FREQ2; tone[i].on_msec = RINGBACK_ON; tone[i].off_msec = RINGBACK_OFF; } tone[RINGBACK_CNT-1].off_msec = RINGBACK_INTERVAL; status = pjmedia_tonegen_play(ringback_port, RINGBACK_CNT, tone, PJMEDIA_TONEGEN_LOOP); if (status != PJ_SUCCESS) goto on_return; status = pjsua_conf_add_port(pool, ringback_port, &ringback_slot); if (status != PJ_SUCCESS) goto on_return; status = pjsua_conf_connect(ringback_slot, 0); if (status != PJ_SUCCESS) goto on_return; key = gui_msgbox(title, "Ringback tone should be playing now in the " "speaker. Press OK to stop. ", WITH_OK); status = PJ_SUCCESS; on_return: if (ringback_slot != -1) pjsua_conf_remove_port(ringback_slot); if (ringback_port) pjmedia_port_destroy(ringback_port); if (pool) pj_pool_release(pool); if (status != PJ_SUCCESS) { systest_perror("Sorry we encounter error when initializing " "the tone generator: ", status); ti->success = PJ_FALSE; pj_strerror(status, ti->reason, sizeof(ti->reason)); } else { key = gui_msgbox(title, "Is the audio okay?", WITH_YESNO); ti->success = (key == KEY_YES); if (!ti->success) pj_ansi_strcpy(ti->reason, USER_ERROR); } return; }