/* * Keep-alive test. */ static int keep_alive_test(pj_stun_config *cfg) { struct stun_srv *srv; struct stun_client *client; pj_sockaddr_in mapped_addr; pj_stun_sock_info info; pj_str_t srv_addr; pj_time_val timeout, t; int i, ret = 0; pj_status_t status; PJ_LOG(3,(THIS_FILE, " normal operation")); status = create_client(cfg, &client, PJ_TRUE); if (status != PJ_SUCCESS) return -310; status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN|WITH_XOR_MAPPED, &srv); if (status != PJ_SUCCESS) { destroy_client(client); return -320; } /* * Part 1: initial Binding resolution. */ PJ_LOG(3,(THIS_FILE, " initial Binding request")); srv_addr = pj_str("127.0.0.1"); status = pj_stun_sock_start(client->sock, &srv_addr, pj_ntohs(srv->addr.ipv4.sin_port), NULL); if (status != PJ_SUCCESS) { destroy_server(srv); destroy_client(client); return -330; } /* Wait until on_status() callback is called with success status */ pj_gettimeofday(&timeout); timeout.sec += 60; do { handle_events(cfg, 100); pj_gettimeofday(&t); } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout)); /* Check that callback with correct operation is called */ if (client->last_op != PJ_STUN_SOCK_BINDING_OP) { PJ_LOG(3,(THIS_FILE, " error: expecting Binding operation status")); ret = -340; goto on_return; } if (client->last_status != PJ_SUCCESS) { PJ_LOG(3,(THIS_FILE, " error: expecting PJ_SUCCESS status")); ret = -350; goto on_return; } /* Check that client doesn't receive anything */ if (client->on_rx_data_cnt != 0) { PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything")); ret = -370; goto on_return; } /* Get info */ pj_bzero(&info, sizeof(info)); pj_stun_sock_get_info(client->sock, &info); /* Check that we have server address */ if (!pj_sockaddr_has_addr(&info.srv_addr)) { PJ_LOG(3,(THIS_FILE, " error: missing server address")); ret = -380; goto on_return; } /* .. and bound address port must not be zero */ if (pj_sockaddr_get_port(&info.bound_addr)==0) { PJ_LOG(3,(THIS_FILE, " error: bound address is zero")); ret = -381; goto on_return; } /* .. and mapped address */ if (!pj_sockaddr_has_addr(&info.mapped_addr)) { PJ_LOG(3,(THIS_FILE, " error: missing mapped address")); ret = -382; goto on_return; } /* verify the mapped address */ pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send); if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) { PJ_LOG(3,(THIS_FILE, " error: mapped address mismatched")); ret = -383; goto on_return; } /* .. and at least one alias */ if (info.alias_cnt == 0) { PJ_LOG(3,(THIS_FILE, " error: must have at least one alias")); ret = -384; goto on_return; } if (!pj_sockaddr_has_addr(&info.aliases[0])) { PJ_LOG(3,(THIS_FILE, " error: missing alias")); ret = -386; goto on_return; } /* * Part 2: sending and receiving data */ PJ_LOG(3,(THIS_FILE, " sending/receiving data")); /* Change server operation mode to echo back data */ srv->flag = ECHO; /* Reset server */ srv->rx_cnt = 0; /* Client sending data to echo server */ { char txt[100]; PJ_LOG(3,(THIS_FILE, " sending to %s", pj_sockaddr_print(&info.srv_addr, txt, sizeof(txt), 3))); } status = pj_stun_sock_sendto(client->sock, NULL, &ret, sizeof(ret), 0, &info.srv_addr, pj_sockaddr_get_len(&info.srv_addr)); if (status != PJ_SUCCESS && status != PJ_EPENDING) { app_perror(" error: server sending data", status); ret = -390; goto on_return; } /* Wait for a short period until client receives data. We can't wait for * too long otherwise the keep-alive will kick in. */ pj_gettimeofday(&timeout); timeout.sec += 1; do { handle_events(cfg, 100); pj_gettimeofday(&t); } while (client->on_rx_data_cnt==0 && PJ_TIME_VAL_LT(t, timeout)); /* Check that data is received in server */ if (srv->rx_cnt == 0) { PJ_LOG(3,(THIS_FILE, " error: server didn't receive data")); ret = -395; goto on_return; } /* Check that status is still OK */ if (client->last_status != PJ_SUCCESS) { app_perror(" error: client has failed", client->last_status); ret = -400; goto on_return; } /* Check that data has been received */ if (client->on_rx_data_cnt == 0) { PJ_LOG(3,(THIS_FILE, " error: client doesn't receive data")); ret = -410; goto on_return; } /* * Part 3: Successful keep-alive, */ PJ_LOG(3,(THIS_FILE, " successful keep-alive scenario")); /* Change server operation mode to normal mode */ srv->flag = RESPOND_STUN | WITH_XOR_MAPPED; /* Reset server */ srv->rx_cnt = 0; /* Reset client */ client->on_status_cnt = 0; client->last_status = PJ_SUCCESS; client->on_rx_data_cnt = 0; /* Wait for keep-alive duration to see if client actually sends the * keep-alive. */ pj_gettimeofday(&timeout); timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1); do { handle_events(cfg, 100); pj_gettimeofday(&t); } while (PJ_TIME_VAL_LT(t, timeout)); /* Check that server receives some packets */ if (srv->rx_cnt == 0) { PJ_LOG(3, (THIS_FILE, " error: no keep-alive was received")); ret = -420; goto on_return; } /* Check that client status is still okay and on_status() callback is NOT * called */ /* No longer valid due to this ticket: * http://trac.pjsip.org/repos/ticket/742 if (client->on_status_cnt != 0) { PJ_LOG(3, (THIS_FILE, " error: on_status() must not be called on successful" "keep-alive when mapped-address does not change")); ret = -430; goto on_return; } */ /* Check that client doesn't receive anything */ if (client->on_rx_data_cnt != 0) { PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything")); ret = -440; goto on_return; } /* * Part 4: Successful keep-alive with IP address change */ PJ_LOG(3,(THIS_FILE, " mapped IP address change")); /* Change server operation mode to normal mode */ srv->flag = RESPOND_STUN | WITH_XOR_MAPPED; /* Change mapped address in the response */ srv->ip_to_send = pj_str("2.2.2.2"); srv->port_to_send++; /* Reset server */ srv->rx_cnt = 0; /* Reset client */ client->on_status_cnt = 0; client->last_status = PJ_SUCCESS; client->on_rx_data_cnt = 0; /* Wait for keep-alive duration to see if client actually sends the * keep-alive. */ pj_gettimeofday(&timeout); timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1); do { handle_events(cfg, 100); pj_gettimeofday(&t); } while (PJ_TIME_VAL_LT(t, timeout)); /* Check that server receives some packets */ if (srv->rx_cnt == 0) { PJ_LOG(3, (THIS_FILE, " error: no keep-alive was received")); ret = -450; goto on_return; } /* Check that on_status() callback is called (because mapped address * has changed) */ if (client->on_status_cnt != 1) { PJ_LOG(3, (THIS_FILE, " error: on_status() was not called")); ret = -460; goto on_return; } /* Check that callback was called with correct operation */ if (client->last_op != PJ_STUN_SOCK_MAPPED_ADDR_CHANGE) { PJ_LOG(3,(THIS_FILE, " error: expecting keep-alive operation status")); ret = -470; goto on_return; } /* Check that last status is still success */ if (client->last_status != PJ_SUCCESS) { PJ_LOG(3, (THIS_FILE, " error: expecting successful status")); ret = -480; goto on_return; } /* Check that client doesn't receive anything */ if (client->on_rx_data_cnt != 0) { PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything")); ret = -490; goto on_return; } /* Get info */ pj_bzero(&info, sizeof(info)); pj_stun_sock_get_info(client->sock, &info); /* Check that we have server address */ if (!pj_sockaddr_has_addr(&info.srv_addr)) { PJ_LOG(3,(THIS_FILE, " error: missing server address")); ret = -500; goto on_return; } /* .. and mapped address */ if (!pj_sockaddr_has_addr(&info.mapped_addr)) { PJ_LOG(3,(THIS_FILE, " error: missing mapped address")); ret = -510; goto on_return; } /* verify the mapped address */ pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send); if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) { PJ_LOG(3,(THIS_FILE, " error: mapped address mismatched")); ret = -520; goto on_return; } /* .. and at least one alias */ if (info.alias_cnt == 0) { PJ_LOG(3,(THIS_FILE, " error: must have at least one alias")); ret = -530; goto on_return; } if (!pj_sockaddr_has_addr(&info.aliases[0])) { PJ_LOG(3,(THIS_FILE, " error: missing alias")); ret = -540; goto on_return; } /* * Part 5: Failed keep-alive */ PJ_LOG(3,(THIS_FILE, " failed keep-alive scenario")); /* Change server operation mode to respond without attribute */ srv->flag = RESPOND_STUN; /* Reset server */ srv->rx_cnt = 0; /* Reset client */ client->on_status_cnt = 0; client->last_status = PJ_SUCCESS; client->on_rx_data_cnt = 0; /* Wait until on_status() is called with failure. */ pj_gettimeofday(&timeout); timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + PJ_STUN_TIMEOUT_VALUE + 5); do { handle_events(cfg, 100); pj_gettimeofday(&t); } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout)); /* Check that callback with correct operation is called */ if (client->last_op != PJ_STUN_SOCK_KEEP_ALIVE_OP) { PJ_LOG(3,(THIS_FILE, " error: expecting keep-alive operation status")); ret = -600; goto on_return; } if (client->last_status == PJ_SUCCESS) { PJ_LOG(3,(THIS_FILE, " error: expecting failed keep-alive")); ret = -610; goto on_return; } /* Check that client doesn't receive anything */ if (client->on_rx_data_cnt != 0) { PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything")); ret = -620; goto on_return; } on_return: destroy_server(srv); destroy_client(client); for (i=0; i<7; ++i) handle_events(cfg, 50); return ret; }
/* * Initialize and start SRTP session with the given parameters. */ PJ_DEF(pj_status_t) pjmedia_transport_srtp_start( pjmedia_transport *tp, const pjmedia_srtp_crypto *tx, const pjmedia_srtp_crypto *rx) { transport_srtp *srtp = (transport_srtp*) tp; srtp_policy_t tx_; srtp_policy_t rx_; err_status_t err; int cr_tx_idx = 0; int au_tx_idx = 0; int cr_rx_idx = 0; int au_rx_idx = 0; int crypto_suites_cnt; pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(tp && tx && rx, PJ_EINVAL); pj_lock_acquire(srtp->mutex); if (srtp->session_inited) { pjmedia_transport_srtp_stop(tp); } crypto_suites_cnt = sizeof(crypto_suites)/sizeof(crypto_suites[0]); /* Get encryption and authentication method */ cr_tx_idx = au_tx_idx = get_crypto_idx(&tx->name); if (tx->flags & PJMEDIA_SRTP_NO_ENCRYPTION) cr_tx_idx = 0; if (tx->flags & PJMEDIA_SRTP_NO_AUTHENTICATION) au_tx_idx = 0; cr_rx_idx = au_rx_idx = get_crypto_idx(&rx->name); if (rx->flags & PJMEDIA_SRTP_NO_ENCRYPTION) cr_rx_idx = 0; if (rx->flags & PJMEDIA_SRTP_NO_AUTHENTICATION) au_rx_idx = 0; /* Check whether the crypto-suite requested is supported */ if (cr_tx_idx == -1 || cr_rx_idx == -1 || au_tx_idx == -1 || au_rx_idx == -1) { status = PJMEDIA_SRTP_ENOTSUPCRYPTO; goto on_return; } /* If all options points to 'NULL' method, just bypass SRTP */ if (cr_tx_idx == 0 && cr_rx_idx == 0 && au_tx_idx == 0 && au_rx_idx == 0) { srtp->bypass_srtp = PJ_TRUE; goto on_return; } /* Check key length */ if (tx->key.slen != (pj_ssize_t)crypto_suites[cr_tx_idx].cipher_key_len || rx->key.slen != (pj_ssize_t)crypto_suites[cr_rx_idx].cipher_key_len) { status = PJMEDIA_SRTP_EINKEYLEN; goto on_return; } /* Init transmit direction */ pj_bzero(&tx_, sizeof(srtp_policy_t)); pj_memmove(srtp->tx_key, tx->key.ptr, tx->key.slen); if (cr_tx_idx && au_tx_idx) tx_.rtp.sec_serv = sec_serv_conf_and_auth; else if (cr_tx_idx) tx_.rtp.sec_serv = sec_serv_conf; else if (au_tx_idx) tx_.rtp.sec_serv = sec_serv_auth; else tx_.rtp.sec_serv = sec_serv_none; tx_.key = (uint8_t*)srtp->tx_key; tx_.ssrc.type = ssrc_any_outbound; tx_.ssrc.value = 0; tx_.rtp.cipher_type = crypto_suites[cr_tx_idx].cipher_type; tx_.rtp.cipher_key_len = crypto_suites[cr_tx_idx].cipher_key_len; tx_.rtp.auth_type = crypto_suites[au_tx_idx].auth_type; tx_.rtp.auth_key_len = crypto_suites[au_tx_idx].auth_key_len; tx_.rtp.auth_tag_len = crypto_suites[au_tx_idx].srtp_auth_tag_len; tx_.rtcp = tx_.rtp; tx_.rtcp.auth_tag_len = crypto_suites[au_tx_idx].srtcp_auth_tag_len; tx_.next = NULL; err = srtp_create(&srtp->srtp_tx_ctx, &tx_); if (err != err_status_ok) { status = PJMEDIA_ERRNO_FROM_LIBSRTP(err); goto on_return; } srtp->tx_policy = *tx; pj_strset(&srtp->tx_policy.key, srtp->tx_key, tx->key.slen); srtp->tx_policy.name=pj_str(crypto_suites[get_crypto_idx(&tx->name)].name); /* Init receive direction */ pj_bzero(&rx_, sizeof(srtp_policy_t)); pj_memmove(srtp->rx_key, rx->key.ptr, rx->key.slen); if (cr_rx_idx && au_rx_idx) rx_.rtp.sec_serv = sec_serv_conf_and_auth; else if (cr_rx_idx) rx_.rtp.sec_serv = sec_serv_conf; else if (au_rx_idx) rx_.rtp.sec_serv = sec_serv_auth; else rx_.rtp.sec_serv = sec_serv_none; rx_.key = (uint8_t*)srtp->rx_key; rx_.ssrc.type = ssrc_any_inbound; rx_.ssrc.value = 0; rx_.rtp.sec_serv = crypto_suites[cr_rx_idx].service; rx_.rtp.cipher_type = crypto_suites[cr_rx_idx].cipher_type; rx_.rtp.cipher_key_len = crypto_suites[cr_rx_idx].cipher_key_len; rx_.rtp.auth_type = crypto_suites[au_rx_idx].auth_type; rx_.rtp.auth_key_len = crypto_suites[au_rx_idx].auth_key_len; rx_.rtp.auth_tag_len = crypto_suites[au_rx_idx].srtp_auth_tag_len; rx_.rtcp = rx_.rtp; rx_.rtcp.auth_tag_len = crypto_suites[au_rx_idx].srtcp_auth_tag_len; rx_.next = NULL; err = srtp_create(&srtp->srtp_rx_ctx, &rx_); if (err != err_status_ok) { srtp_dealloc(srtp->srtp_tx_ctx); status = PJMEDIA_ERRNO_FROM_LIBSRTP(err); goto on_return; } srtp->rx_policy = *rx; pj_strset(&srtp->rx_policy.key, srtp->rx_key, rx->key.slen); srtp->rx_policy.name=pj_str(crypto_suites[get_crypto_idx(&rx->name)].name); /* Declare SRTP session initialized */ srtp->session_inited = PJ_TRUE; PJ_LOG(5, (srtp->pool->obj_name, "TX: %s key=%s", srtp->tx_policy.name.ptr, octet_string_hex_string(tx->key.ptr, tx->key.slen))); if (srtp->tx_policy.flags) { PJ_LOG(5,(srtp->pool->obj_name,"TX: disable%s%s", (cr_tx_idx?"":" enc"), (au_tx_idx?"":" auth"))); } PJ_LOG(5, (srtp->pool->obj_name, "RX: %s key=%s", srtp->rx_policy.name.ptr, octet_string_hex_string(rx->key.ptr, rx->key.slen))); if (srtp->rx_policy.flags) { PJ_LOG(5,(srtp->pool->obj_name,"RX: disable%s%s", (cr_rx_idx?"":" enc"), (au_rx_idx?"":" auth"))); } on_return: pj_lock_release(srtp->mutex); return status; }
/* Get IP interface for sending to the specified destination */ PJ_DEF(pj_status_t) pj_getipinterface(int af, const pj_str_t *dst, pj_sockaddr *itf_addr, pj_bool_t allow_resolve, pj_sockaddr *p_dst_addr) { pj_sockaddr dst_addr; pj_sock_t fd; int len; pj_uint8_t zero[64]; pj_status_t status; pj_sockaddr_init(af, &dst_addr, NULL, 53); status = pj_inet_pton(af, dst, pj_sockaddr_get_addr(&dst_addr)); if (status != PJ_SUCCESS) { /* "dst" is not an IP address. */ if (allow_resolve) { status = pj_sockaddr_init(af, &dst_addr, dst, 53); } else { pj_str_t cp; if (af == PJ_AF_INET) { cp = pj_str("1.1.1.1"); } else { cp = pj_str("1::1"); } status = pj_sockaddr_init(af, &dst_addr, &cp, 53); } if (status != PJ_SUCCESS) return status; } /* Create UDP socket and connect() to the destination IP */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &fd); if (status != PJ_SUCCESS) { return status; } status = pj_sock_connect(fd, &dst_addr, pj_sockaddr_get_len(&dst_addr)); if (status != PJ_SUCCESS) { pj_sock_close(fd); return status; } len = sizeof(*itf_addr); status = pj_sock_getsockname(fd, itf_addr, &len); if (status != PJ_SUCCESS) { pj_sock_close(fd); return status; } pj_sock_close(fd); /* Check that the address returned is not zero */ pj_bzero(zero, sizeof(zero)); if (pj_memcmp(pj_sockaddr_get_addr(itf_addr), zero, pj_sockaddr_get_addr_len(itf_addr))==0) { return PJ_ENOTFOUND; } if (p_dst_addr) *p_dst_addr = dst_addr; return PJ_SUCCESS; }
//Wrap start & stop PJ_DECL(pj_status_t) csipsimple_init(pjsua_config *ua_cfg, pjsua_logging_config *log_cfg, pjsua_media_config *media_cfg, csipsimple_config *css_cfg, jobject context) { pj_status_t result; /* Create memory pool for application. */ css_var.pool = pjsua_pool_create("css", 1000, 1000); PJ_ASSERT_RETURN(css_var.pool, PJ_ENOMEM); // Finalize configuration log_cfg->cb = &pj_android_log_msg; if (css_cfg->turn_username.slen) { media_cfg->turn_auth_cred.type = PJ_STUN_AUTH_CRED_STATIC; media_cfg->turn_auth_cred.data.static_cred.realm = pj_str("*"); pj_strdup_with_null(css_var.pool, &media_cfg->turn_auth_cred.data.static_cred.username, &css_cfg->turn_username); if (css_cfg->turn_password.slen) { media_cfg->turn_auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN; pj_strdup_with_null(css_var.pool, &media_cfg->turn_auth_cred.data.static_cred.data, &css_cfg->turn_password); } } // Static cfg extern pj_bool_t pjsip_use_compact_form; extern pj_bool_t pjsip_include_allow_hdr_in_dlg; extern pj_bool_t pjmedia_add_rtpmap_for_static_pt; extern pj_bool_t pjsua_no_update; pjsua_no_update = css_cfg->use_no_update ? PJ_TRUE : PJ_FALSE; pjsip_use_compact_form = css_cfg->use_compact_form_headers ? PJ_TRUE : PJ_FALSE; /* do not transmit Allow header */ pjsip_include_allow_hdr_in_dlg = css_cfg->use_compact_form_headers ? PJ_FALSE : PJ_TRUE; /* Do not include rtpmap for static payload types (<96) */ pjmedia_add_rtpmap_for_static_pt = css_cfg->use_compact_form_sdp ? PJ_FALSE : PJ_TRUE; css_tcp_keep_alive_interval = css_cfg->tcp_keep_alive_interval; css_tls_keep_alive_interval = css_cfg->tls_keep_alive_interval; // Audio codec cfg css_var.extra_aud_codecs_cnt = css_cfg->extra_aud_codecs_cnt; unsigned i; for (i = 0; i < css_cfg->extra_aud_codecs_cnt; i++) { dynamic_factory *css_codec = &css_var.extra_aud_codecs[i]; dynamic_factory *cfg_codec = &css_cfg->extra_aud_codecs[i]; pj_strdup_with_null(css_var.pool, &css_codec->shared_lib_path, &cfg_codec->shared_lib_path); pj_strdup_with_null(css_var.pool, &css_codec->init_factory_name, &cfg_codec->init_factory_name); } // Video codec cfg -- For now only destroy is useful but for future // hopefully vid codec mgr will behaves as audio does // Also in this case destroy will become obsolete css_var.extra_vid_codecs_cnt = css_cfg->extra_vid_codecs_cnt; for (i = 0; i < css_cfg->extra_vid_codecs_cnt; i++) { dynamic_factory *css_codec = &css_var.extra_vid_codecs[i]; dynamic_factory *cfg_codec = &css_cfg->extra_vid_codecs[i]; pj_strdup_with_null(css_var.pool, &css_codec->shared_lib_path, &cfg_codec->shared_lib_path); pj_strdup_with_null(css_var.pool, &css_codec->init_factory_name, &cfg_codec->init_factory_name); css_codec = &css_var.extra_vid_codecs_destroy[i]; cfg_codec = &css_cfg->extra_vid_codecs_destroy[i]; pj_strdup_with_null(css_var.pool, &css_codec->shared_lib_path, &cfg_codec->shared_lib_path); pj_strdup_with_null(css_var.pool, &css_codec->init_factory_name, &cfg_codec->init_factory_name); } // ZRTP cfg css_var.default_use_zrtp = css_cfg->use_zrtp; ua_cfg->cb.on_create_media_transport = &on_transport_created_wrapper; #if defined(PJMEDIA_HAS_ZRTP) && PJMEDIA_HAS_ZRTP!=0 pj_ansi_snprintf(css_var.zid_file, sizeof(css_var.zid_file), "%.*s/simple.zid", css_cfg->storage_folder.slen, css_cfg->storage_folder.ptr); #endif JNIEnv *jni_env = 0; ATTACH_JVM(jni_env); css_var.context = (*jni_env)->NewGlobalRef(jni_env, context); DETACH_JVM(jni_env); result = (pj_status_t) pjsua_init(ua_cfg, log_cfg, media_cfg); if (result == PJ_SUCCESS) { init_ringback_tone(); // Init audio device pj_status_t added_audio = PJ_ENOTFOUND; if (css_cfg->audio_implementation.init_factory_name.slen > 0) { pjmedia_aud_dev_factory* (*init_factory)( pj_pool_factory *pf) = get_library_factory(&css_cfg->audio_implementation); if(init_factory != NULL) { pjmedia_aud_register_factory(init_factory); added_audio = PJ_SUCCESS; PJ_LOG(4, (THIS_FILE, "Loaded audio dev")); } } // Fallback to default audio dev if no one found if (added_audio != PJ_SUCCESS) { pjmedia_aud_register_factory(&pjmedia_android_factory); } // Init video device #if PJMEDIA_HAS_VIDEO // load renderer if (css_cfg->video_render_implementation.init_factory_name.slen > 0) { pjmedia_vid_dev_factory* (*init_factory)( pj_pool_factory *pf) = get_library_factory(&css_cfg->video_render_implementation); if(init_factory != NULL) { pjmedia_vid_register_factory(init_factory); PJ_LOG(4, (THIS_FILE, "Loaded video render dev")); } } // load capture if (css_cfg->video_capture_implementation.init_factory_name.slen > 0) { pjmedia_vid_dev_factory* (*init_factory)( pj_pool_factory *pf) = get_library_factory(&css_cfg->video_capture_implementation); if(init_factory != NULL) { pjmedia_vid_register_factory(init_factory); PJ_LOG(4, (THIS_FILE, "Loaded video capture dev")); } } // Load ffmpeg converter pjmedia_converter_mgr* cvrt_mgr = pjmedia_converter_mgr_instance(); if(css_cfg->vid_converter.init_factory_name.slen > 0){ pj_status_t (*init_factory)(pjmedia_converter_mgr* cvrt_mgr) = get_library_factory(&css_cfg->vid_converter); if(init_factory != NULL) { init_factory(cvrt_mgr); PJ_LOG(4, (THIS_FILE, "Loaded video converter")); } } // Load video codecs pjmedia_vid_codec_mgr* vid_mgr = pjmedia_vid_codec_mgr_instance(); for (i = 0; i < css_var.extra_vid_codecs_cnt; i++) { dynamic_factory *codec = &css_var.extra_vid_codecs[i]; pj_status_t (*init_factory)(pjmedia_vid_codec_mgr *mgr, pj_pool_factory *pf) = get_library_factory(codec); if(init_factory != NULL){ pj_status_t status = init_factory(vid_mgr, &pjsua_var.cp.factory); if(status != PJ_SUCCESS) { PJ_LOG(2, (THIS_FILE,"Error loading dynamic codec plugin")); } } } #endif } return result; }
/* Parse crypto attribute line */ static pj_status_t parse_attr_crypto(pj_pool_t *pool, const pjmedia_sdp_attr *attr, pjmedia_srtp_crypto *crypto, int *tag) { pj_str_t input; char *token; int token_len; pj_str_t tmp; pj_status_t status; int itmp; pj_bzero(crypto, sizeof(*crypto)); pj_strdup_with_null(pool, &input, &attr->value); /* Tag */ token = strtok(input.ptr, " "); if (!token) { PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting tag")); return PJMEDIA_SDP_EINATTR; } token_len = pj_ansi_strlen(token); /* Tag must not use leading zeroes. */ if (token_len > 1 && *token == '0') return PJMEDIA_SDP_EINATTR; /* Tag must be decimal, i.e: contains only digit '0'-'9'. */ for (itmp = 0; itmp < token_len; ++itmp) if (!pj_isdigit(token[itmp])) return PJMEDIA_SDP_EINATTR; /* Get tag value. */ *tag = atoi(token); /* Crypto-suite */ token = strtok(NULL, " "); if (!token) { PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting crypto suite")); return PJMEDIA_SDP_EINATTR; } crypto->name = pj_str(token); /* Key method */ token = strtok(NULL, ":"); if (!token) { PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key method")); return PJMEDIA_SDP_EINATTR; } if (pj_ansi_stricmp(token, "inline")) { PJ_LOG(4,(THIS_FILE, "Attribute crypto key method '%s' not supported!", token)); return PJMEDIA_SDP_EINATTR; } /* Key */ token = strtok(NULL, "| "); if (!token) { PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key")); return PJMEDIA_SDP_EINATTR; } tmp = pj_str(token); if (PJ_BASE64_TO_BASE256_LEN(tmp.slen) > MAX_KEY_LEN) { PJ_LOG(4,(THIS_FILE, "Key too long")); return PJMEDIA_SRTP_EINKEYLEN; } /* Decode key */ crypto->key.ptr = (char*) pj_pool_zalloc(pool, MAX_KEY_LEN); itmp = MAX_KEY_LEN; status = pj_base64_decode(&tmp, (pj_uint8_t*)crypto->key.ptr, &itmp); if (status != PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "Failed decoding crypto key from base64")); return status; } crypto->key.slen = itmp; return PJ_SUCCESS; }
static int init() { int i; pj_status_t status; CHECK( pj_init() ); CHECK( pjlib_util_init() ); CHECK( pjnath_init() ); /* Check that server is specified */ if (!o.srv_addr) { printf("Error: server must be specified\n"); return PJ_EINVAL; } pj_caching_pool_init(&g.cp, &pj_pool_factory_default_policy, 0); g.pool = pj_pool_create(&g.cp.factory, "main", 1000, 1000, NULL); /* Init global STUN config */ pj_stun_config_init(&g.stun_config, &g.cp.factory, 0, NULL, NULL); /* Create global timer heap */ CHECK( pj_timer_heap_create(g.pool, 1000, &g.stun_config.timer_heap) ); /* Create global ioqueue */ CHECK( pj_ioqueue_create(g.pool, 16, &g.stun_config.ioqueue) ); /* * Create peers */ for (i=0; i<(int)PJ_ARRAY_SIZE(g.peer); ++i) { pj_stun_sock_cb stun_sock_cb; char name[] = "peer0"; pj_uint16_t port; pj_str_t server; pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb)); stun_sock_cb.on_rx_data = &stun_sock_on_rx_data; stun_sock_cb.on_status = &stun_sock_on_status; g.peer[i].mapped_addr.addr.sa_family = pj_AF_INET(); name[strlen(name)-1] = '0'+i; status = pj_stun_sock_create(&g.stun_config, name, pj_AF_INET(), &stun_sock_cb, NULL, &g.peer[i], &g.peer[i].stun_sock); if (status != PJ_SUCCESS) { my_perror("pj_stun_sock_create()", status); return status; } if (o.stun_server) { server = pj_str(o.stun_server); port = PJ_STUN_PORT; } else { server = pj_str(o.srv_addr); port = (pj_uint16_t)(o.srv_port?atoi(o.srv_port):PJ_STUN_PORT); } status = pj_stun_sock_start(g.peer[i].stun_sock, &server, port, NULL); if (status != PJ_SUCCESS) { my_perror("pj_stun_sock_start()", status); return status; } } /* Start the worker thread */ CHECK( pj_thread_create(g.pool, "stun", &worker_thread, NULL, 0, 0, &g.thread) ); return PJ_SUCCESS; }
/* Callback to be called to handle transaction state changed. */ static void tu_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event) { struct uac_data *uac_data; pj_status_t status; if (tsx->role == PJSIP_ROLE_UAS) { if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { struct uas_data *uas_data; uas_data = (struct uas_data*) tsx->mod_data[mod_tu.id]; if (uas_data->uac_tsx) { uac_data = (struct uac_data*) uas_data->uac_tsx->mod_data[mod_tu.id]; uac_data->uas_tsx = NULL; } } return; } /* Get the data that we attached to the UAC transaction previously */ uac_data = (struct uac_data*) tsx->mod_data[mod_tu.id]; /* Handle incoming response */ if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { pjsip_rx_data *rdata; pjsip_response_addr res_addr; pjsip_via_hdr *hvia; pjsip_tx_data *tdata; rdata = event->body.tsx_state.src.rdata; /* Do not forward 100 response for INVITE (we already responded * INVITE with 100) */ if (tsx->method.id == PJSIP_INVITE_METHOD && rdata->msg_info.msg->line.status.code == 100) { return; } /* Create response to be forwarded upstream * (Via will be stripped here) */ status = pjsip_endpt_create_response_fwd(global.endpt, rdata, 0, &tdata); if (status != PJ_SUCCESS) { app_perror("Error creating response", status); return; } /* Get topmost Via header of the new response */ hvia = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); if (hvia == NULL) { /* Invalid response! Just drop it */ pjsip_tx_data_dec_ref(tdata); return; } /* Calculate the address to forward the response */ pj_bzero(&res_addr, sizeof(res_addr)); res_addr.dst_host.type = PJSIP_TRANSPORT_UDP; res_addr.dst_host.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_UDP); /* Destination address is Via's received param */ res_addr.dst_host.addr.host = hvia->recvd_param; if (res_addr.dst_host.addr.host.slen == 0) { /* Someone has messed up our Via header! */ res_addr.dst_host.addr.host = hvia->sent_by.host; } /* Destination port is the rport */ if (hvia->rport_param != 0 && hvia->rport_param != -1) res_addr.dst_host.addr.port = hvia->rport_param; if (res_addr.dst_host.addr.port == 0) { /* Ugh, original sender didn't put rport! * At best, can only send the response to the port in Via. */ res_addr.dst_host.addr.port = hvia->sent_by.port; } /* Forward response with the UAS transaction */ pjsip_tsx_send_msg(uac_data->uas_tsx, tdata); } /* If UAC transaction is terminated, terminate the UAS as well. * This could happen because of: * - timeout on the UAC side * - receipt of 2xx response to INVITE */ if (tsx->state == PJSIP_TSX_STATE_TERMINATED && uac_data && uac_data->uas_tsx) { pjsip_transaction *uas_tsx; struct uas_data *uas_data; uas_tsx = uac_data->uas_tsx; uas_data = (struct uas_data*) uas_tsx->mod_data[mod_tu.id]; uas_data->uac_tsx = NULL; if (event->body.tsx_state.type == PJSIP_EVENT_TIMER) { /* Send 408/Timeout if this is an INVITE transaction, since * we must have sent provisional response before. For non * INVITE transaction, just destroy it. */ if (tsx->method.id == PJSIP_INVITE_METHOD) { pjsip_tx_data *tdata = uas_tsx->last_tx; tdata->msg->line.status.code = PJSIP_SC_REQUEST_TIMEOUT; tdata->msg->line.status.reason = pj_str("Request timed out"); tdata->msg->body = NULL; pjsip_tx_data_add_ref(tdata); pjsip_tx_data_invalidate_msg(tdata); pjsip_tsx_send_msg(uas_tsx, tdata); } else { /* For non-INVITE, just destroy the UAS transaction */ pjsip_tsx_terminate(uas_tsx, PJSIP_SC_REQUEST_TIMEOUT); } } else if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { if (uas_tsx->state < PJSIP_TSX_STATE_TERMINATED) { pjsip_msg *msg; int code; msg = event->body.tsx_state.src.rdata->msg_info.msg; code = msg->line.status.code; uac_data->uas_tsx = NULL; pjsip_tsx_terminate(uas_tsx, code); } } } }
/* * Create stream based on the codec, dir, remote address, etc. */ static pj_status_t create_stream( pj_pool_t *pool, pjmedia_endpt *med_endpt, const pjmedia_codec_info *codec_info, pjmedia_dir dir, pj_uint16_t local_port, const pj_sockaddr_in *rem_addr, #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) pj_bool_t use_srtp, const pj_str_t *crypto_suite, const pj_str_t *srtp_tx_key, const pj_str_t *srtp_rx_key, #endif pjmedia_stream **p_stream ) { pjmedia_stream_info info; pjmedia_transport *transport = NULL; pj_status_t status; #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) pjmedia_transport *srtp_tp = NULL; #endif /* Reset stream info. */ pj_bzero(&info, sizeof(info)); /* Initialize stream info formats */ info.type = PJMEDIA_TYPE_AUDIO; info.dir = dir; pj_memcpy(&info.fmt, codec_info, sizeof(pjmedia_codec_info)); info.tx_pt = codec_info->pt; info.rx_pt = codec_info->pt; info.ssrc = pj_rand(); #if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR /* Set default RTCP XR enabled/disabled */ info.rtcp_xr_enabled = PJ_TRUE; #endif /* Copy remote address */ pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in)); /* If remote address is not set, set to an arbitrary address * (otherwise stream will assert). */ if (info.rem_addr.addr.sa_family == 0) { const pj_str_t addr = pj_str("127.0.0.1"); pj_sockaddr_in_init(&info.rem_addr.ipv4, &addr, 0); } /* Create media transport */ status = pjmedia_transport_udp_create(med_endpt, NULL, local_port, 0, &transport); if (status != PJ_SUCCESS) return status; #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) /* Check if SRTP enabled */ if (use_srtp) { pjmedia_srtp_crypto tx_plc, rx_plc; status = pjmedia_transport_srtp_create(med_endpt, transport, NULL, &srtp_tp); if (status != PJ_SUCCESS) return status; pj_bzero(&tx_plc, sizeof(pjmedia_srtp_crypto)); pj_bzero(&rx_plc, sizeof(pjmedia_srtp_crypto)); tx_plc.key = *srtp_tx_key; tx_plc.name = *crypto_suite; rx_plc.key = *srtp_rx_key; rx_plc.name = *crypto_suite; status = pjmedia_transport_srtp_start(srtp_tp, &tx_plc, &rx_plc); if (status != PJ_SUCCESS) return status; transport = srtp_tp; } #endif /* Now that the stream info is initialized, we can create the * stream. */ status = pjmedia_stream_create( med_endpt, pool, &info, transport, NULL, p_stream); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error creating stream", status); pjmedia_transport_close(transport); return status; } return PJ_SUCCESS; }
/* * main() */ int main(int argc, char *argv[]) { pj_caching_pool cp; pjmedia_endpt *med_endpt; pj_pool_t *pool; pjmedia_port *rec_file_port = NULL, *play_file_port = NULL; pjmedia_master_port *master_port = NULL; pjmedia_snd_port *snd_port = NULL; pjmedia_stream *stream = NULL; pjmedia_port *stream_port; char tmp[10]; pj_status_t status; #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) /* SRTP variables */ pj_bool_t use_srtp = PJ_FALSE; char tmp_tx_key[64]; char tmp_rx_key[64]; pj_str_t srtp_tx_key = {NULL, 0}; pj_str_t srtp_rx_key = {NULL, 0}; pj_str_t srtp_crypto_suite = {NULL, 0}; int tmp_key_len; #endif /* Default values */ const pjmedia_codec_info *codec_info; pjmedia_codec_param codec_param; pjmedia_dir dir = PJMEDIA_DIR_DECODING; pj_sockaddr_in remote_addr; pj_uint16_t local_port = 4000; char *codec_id = NULL; char *rec_file = NULL; char *play_file = NULL; enum { OPT_CODEC = 'c', OPT_LOCAL_PORT = 'p', OPT_REMOTE = 'r', OPT_PLAY_FILE = 'w', OPT_RECORD_FILE = 'R', OPT_SEND_RECV = 'b', OPT_SEND_ONLY = 's', OPT_RECV_ONLY = 'i', #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) OPT_USE_SRTP = 'S', #endif OPT_SRTP_TX_KEY = 'x', OPT_SRTP_RX_KEY = 'y', OPT_HELP = 'h', }; struct pj_getopt_option long_options[] = { { "codec", 1, 0, OPT_CODEC }, { "local-port", 1, 0, OPT_LOCAL_PORT }, { "remote", 1, 0, OPT_REMOTE }, { "play-file", 1, 0, OPT_PLAY_FILE }, { "record-file", 1, 0, OPT_RECORD_FILE }, { "send-recv", 0, 0, OPT_SEND_RECV }, { "send-only", 0, 0, OPT_SEND_ONLY }, { "recv-only", 0, 0, OPT_RECV_ONLY }, #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) { "use-srtp", 2, 0, OPT_USE_SRTP }, { "srtp-tx-key", 1, 0, OPT_SRTP_TX_KEY }, { "srtp-rx-key", 1, 0, OPT_SRTP_RX_KEY }, #endif { "help", 0, 0, OPT_HELP }, { NULL, 0, 0, 0 }, }; int c; int option_index; pj_bzero(&remote_addr, sizeof(remote_addr)); /* init PJLIB : */ status = pj_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Parse arguments */ pj_optind = 0; while((c=pj_getopt_long(argc,argv, "h", long_options, &option_index))!=-1) { switch (c) { case OPT_CODEC: codec_id = pj_optarg; break; case OPT_LOCAL_PORT: local_port = (pj_uint16_t) atoi(pj_optarg); if (local_port < 1) { printf("Error: invalid local port %s\n", pj_optarg); return 1; } break; case OPT_REMOTE: { pj_str_t ip = pj_str(strtok(pj_optarg, ":")); pj_uint16_t port = (pj_uint16_t) atoi(strtok(NULL, ":")); status = pj_sockaddr_in_init(&remote_addr, &ip, port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Invalid remote address", status); return 1; } } break; case OPT_PLAY_FILE: play_file = pj_optarg; break; case OPT_RECORD_FILE: rec_file = pj_optarg; break; case OPT_SEND_RECV: dir = PJMEDIA_DIR_ENCODING_DECODING; break; case OPT_SEND_ONLY: dir = PJMEDIA_DIR_ENCODING; break; case OPT_RECV_ONLY: dir = PJMEDIA_DIR_DECODING; break; #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) case OPT_USE_SRTP: use_srtp = PJ_TRUE; if (pj_optarg) { pj_strset(&srtp_crypto_suite, pj_optarg, strlen(pj_optarg)); } else { srtp_crypto_suite = pj_str("AES_CM_128_HMAC_SHA1_80"); } break; case OPT_SRTP_TX_KEY: tmp_key_len = hex_string_to_octet_string(tmp_tx_key, pj_optarg, (int)strlen(pj_optarg)); pj_strset(&srtp_tx_key, tmp_tx_key, tmp_key_len/2); break; case OPT_SRTP_RX_KEY: tmp_key_len = hex_string_to_octet_string(tmp_rx_key, pj_optarg, (int)strlen(pj_optarg)); pj_strset(&srtp_rx_key, tmp_rx_key, tmp_key_len/2); break; #endif case OPT_HELP: usage(); return 1; default: printf("Invalid options %s\n", argv[pj_optind]); return 1; } } /* Verify arguments. */ if (dir & PJMEDIA_DIR_ENCODING) { if (remote_addr.sin_addr.s_addr == 0) { printf("Error: remote address must be set\n"); return 1; } } if (play_file != NULL && dir != PJMEDIA_DIR_ENCODING) { printf("Direction is set to --send-only because of --play-file\n"); dir = PJMEDIA_DIR_ENCODING; } #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) /* SRTP validation */ if (use_srtp) { if (!srtp_tx_key.slen || !srtp_rx_key.slen) { printf("Error: Key for each SRTP stream direction must be set\n"); return 1; } } #endif /* Must create a pool factory before we can allocate any memory. */ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); /* * Initialize media endpoint. * This will implicitly initialize PJMEDIA too. */ status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Create memory pool for application purpose */ pool = pj_pool_create( &cp.factory, /* pool factory */ "app", /* pool name. */ 4000, /* init size */ 4000, /* increment size */ NULL /* callback on error */ ); /* Register all supported codecs */ status = init_codecs(med_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Find which codec to use. */ if (codec_id) { unsigned count = 1; pj_str_t str_codec_id = pj_str(codec_id); pjmedia_codec_mgr *codec_mgr = pjmedia_endpt_get_codec_mgr(med_endpt); status = pjmedia_codec_mgr_find_codecs_by_id( codec_mgr, &str_codec_id, &count, &codec_info, NULL); if (status != PJ_SUCCESS) { printf("Error: unable to find codec %s\n", codec_id); return 1; } } else { /* Default to pcmu */ pjmedia_codec_mgr_get_codec_info( pjmedia_endpt_get_codec_mgr(med_endpt), 0, &codec_info); } /* Create stream based on program arguments */ status = create_stream(pool, med_endpt, codec_info, dir, local_port, &remote_addr, #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) use_srtp, &srtp_crypto_suite, &srtp_tx_key, &srtp_rx_key, #endif &stream); if (status != PJ_SUCCESS) goto on_exit; /* Get codec default param for info */ status = pjmedia_codec_mgr_get_default_param( pjmedia_endpt_get_codec_mgr(med_endpt), codec_info, &codec_param); /* Should be ok, as create_stream() above succeeded */ pj_assert(status == PJ_SUCCESS); /* Get the port interface of the stream */ status = pjmedia_stream_get_port( stream, &stream_port); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); if (play_file) { unsigned wav_ptime; wav_ptime = PJMEDIA_PIA_PTIME(&stream_port->info); status = pjmedia_wav_player_port_create(pool, play_file, wav_ptime, 0, -1, &play_file_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to use file", status); goto on_exit; } status = pjmedia_master_port_create(pool, play_file_port, stream_port, 0, &master_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create master port", status); goto on_exit; } status = pjmedia_master_port_start(master_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error starting master port", status); goto on_exit; } printf("Playing from WAV file %s..\n", play_file); } else if (rec_file) { status = pjmedia_wav_writer_port_create(pool, rec_file, PJMEDIA_PIA_SRATE(&stream_port->info), PJMEDIA_PIA_CCNT(&stream_port->info), PJMEDIA_PIA_SPF(&stream_port->info), PJMEDIA_PIA_BITS(&stream_port->info), 0, 0, &rec_file_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to use file", status); goto on_exit; } status = pjmedia_master_port_create(pool, stream_port, rec_file_port, 0, &master_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create master port", status); goto on_exit; } status = pjmedia_master_port_start(master_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error starting master port", status); goto on_exit; } printf("Recording to WAV file %s..\n", rec_file); } else { /* Create sound device port. */ if (dir == PJMEDIA_DIR_ENCODING_DECODING) status = pjmedia_snd_port_create(pool, -1, -1, PJMEDIA_PIA_SRATE(&stream_port->info), PJMEDIA_PIA_CCNT(&stream_port->info), PJMEDIA_PIA_SPF(&stream_port->info), PJMEDIA_PIA_BITS(&stream_port->info), 0, &snd_port); else if (dir == PJMEDIA_DIR_ENCODING) status = pjmedia_snd_port_create_rec(pool, -1, PJMEDIA_PIA_SRATE(&stream_port->info), PJMEDIA_PIA_CCNT(&stream_port->info), PJMEDIA_PIA_SPF(&stream_port->info), PJMEDIA_PIA_BITS(&stream_port->info), 0, &snd_port); else status = pjmedia_snd_port_create_player(pool, -1, PJMEDIA_PIA_SRATE(&stream_port->info), PJMEDIA_PIA_CCNT(&stream_port->info), PJMEDIA_PIA_SPF(&stream_port->info), PJMEDIA_PIA_BITS(&stream_port->info), 0, &snd_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create sound port", status); goto on_exit; } /* Connect sound port to stream */ status = pjmedia_snd_port_connect( snd_port, stream_port ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); } /* Start streaming */ pjmedia_stream_start(stream); /* Done */ if (dir == PJMEDIA_DIR_DECODING) printf("Stream is active, dir is recv-only, local port is %d\n", local_port); else if (dir == PJMEDIA_DIR_ENCODING) printf("Stream is active, dir is send-only, sending to %s:%d\n", pj_inet_ntoa(remote_addr.sin_addr), pj_ntohs(remote_addr.sin_port)); else printf("Stream is active, send/recv, local port is %d, " "sending to %s:%d\n", local_port, pj_inet_ntoa(remote_addr.sin_addr), pj_ntohs(remote_addr.sin_port)); for (;;) { puts(""); puts("Commands:"); puts(" s Display media statistics"); puts(" q Quit"); puts(""); printf("Command: "); fflush(stdout); if (fgets(tmp, sizeof(tmp), stdin) == NULL) { puts("EOF while reading stdin, will quit now.."); break; } if (tmp[0] == 's') print_stream_stat(stream, &codec_param); else if (tmp[0] == 'q') break; } /* Start deinitialization: */ on_exit: /* Destroy sound device */ if (snd_port) { pjmedia_snd_port_destroy( snd_port ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); } /* If there is master port, then we just need to destroy master port * (it will recursively destroy upstream and downstream ports, which * in this case are file_port and stream_port). */ if (master_port) { pjmedia_master_port_destroy(master_port, PJ_TRUE); play_file_port = NULL; stream = NULL; } /* Destroy stream */ if (stream) { pjmedia_transport *tp; tp = pjmedia_stream_get_transport(stream); pjmedia_stream_destroy(stream); pjmedia_transport_close(tp); } /* Destroy file ports */ if (play_file_port) pjmedia_port_destroy( play_file_port ); if (rec_file_port) pjmedia_port_destroy( rec_file_port ); /* Release application pool */ pj_pool_release( pool ); /* Destroy media endpoint. */ pjmedia_endpt_destroy( med_endpt ); /* Destroy pool factory */ pj_caching_pool_destroy( &cp ); /* Shutdown PJLIB */ pj_shutdown(); return (status == PJ_SUCCESS) ? 0 : 1; }
/* * Parse fmtp for specified format/payload type. */ static void parse_fmtp( pj_pool_t *pool, const pjmedia_sdp_media *m, unsigned pt, pjmedia_codec_fmtp *fmtp) { const pjmedia_sdp_attr *attr; pjmedia_sdp_fmtp sdp_fmtp; char *p, *p_end, fmt_buf[8]; pj_str_t fmt; pj_assert(m && fmtp); pj_bzero(fmtp, sizeof(pjmedia_codec_fmtp)); /* Get "fmtp" attribute for the format */ pj_ansi_sprintf(fmt_buf, "%d", pt); fmt = pj_str(fmt_buf); attr = pjmedia_sdp_media_find_attr2(m, "fmtp", &fmt); if (attr == NULL) return; /* Parse "fmtp" attribute */ if (pjmedia_sdp_attr_get_fmtp(attr, &sdp_fmtp) != PJ_SUCCESS) return; /* Prepare parsing */ p = sdp_fmtp.fmt_param.ptr; p_end = p + sdp_fmtp.fmt_param.slen; /* Parse */ while (p < p_end) { char *token, *start, *end; /* Skip whitespaces */ while (p < p_end && (*p == ' ' || *p == '\t')) ++p; if (p == p_end) break; /* Get token */ start = p; while (p < p_end && *p != ';' && *p != '=') ++p; end = p - 1; /* Right trim */ while (end >= start && (*end == ' ' || *end == '\t' || *end == '\r' || *end == '\n' )) --end; /* Forward a char after trimming */ ++end; /* Store token */ if (end > start) { token = (char*)pj_pool_alloc(pool, end - start); pj_ansi_strncpy(token, start, end - start); if (*p == '=') /* Got param name */ pj_strset(&fmtp->param[fmtp->cnt].name, token, end - start); else /* Got param value */ pj_strset(&fmtp->param[fmtp->cnt++].val, token, end - start); } else if (*p != '=') { ++fmtp->cnt; } /* Next */ ++p; } }
static int https_client_test(unsigned ms_timeout) { pj_pool_t *pool = NULL; pj_ioqueue_t *ioqueue = NULL; pj_timer_heap_t *timer = NULL; pj_ssl_sock_t *ssock = NULL; pj_ssl_sock_param param; pj_status_t status; struct test_state state = {0}; pj_sockaddr local_addr, rem_addr; pj_str_t tmp_st; pool = pj_pool_create(mem, "https_get", 256, 256, NULL); status = pj_ioqueue_create(pool, 4, &ioqueue); if (status != PJ_SUCCESS) { goto on_return; } status = pj_timer_heap_create(pool, 4, &timer); if (status != PJ_SUCCESS) { goto on_return; } state.pool = pool; state.send_str = HTTP_REQ; state.send_str_len = pj_ansi_strlen(state.send_str); state.is_verbose = PJ_TRUE; pj_ssl_sock_param_default(¶m); param.cb.on_connect_complete = &ssl_on_connect_complete; param.cb.on_data_read = &ssl_on_data_read; param.cb.on_data_sent = &ssl_on_data_sent; param.ioqueue = ioqueue; param.user_data = &state; param.server_name = pj_str((char*)HTTP_SERVER_ADDR); param.timer_heap = timer; param.timeout.sec = 0; param.timeout.msec = ms_timeout; param.proto = PJ_SSL_SOCK_PROTO_SSL23; pj_time_val_normalize(¶m.timeout); status = pj_ssl_sock_create(pool, ¶m, &ssock); if (status != PJ_SUCCESS) { goto on_return; } pj_sockaddr_init(PJ_AF_INET, &local_addr, pj_strset2(&tmp_st, "0.0.0.0"), 0); pj_sockaddr_init(PJ_AF_INET, &rem_addr, pj_strset2(&tmp_st, HTTP_SERVER_ADDR), HTTP_SERVER_PORT); status = pj_ssl_sock_start_connect(ssock, pool, &local_addr, &rem_addr, sizeof(rem_addr)); if (status == PJ_SUCCESS) { ssl_on_connect_complete(ssock, PJ_SUCCESS); } else if (status == PJ_EPENDING) { status = PJ_SUCCESS; } else { goto on_return; } /* Wait until everything has been sent/received */ while (state.err == PJ_SUCCESS && !state.done) { #ifdef PJ_SYMBIAN pj_symbianos_poll(-1, 1000); #else pj_time_val delay = {0, 100}; pj_ioqueue_poll(ioqueue, &delay); pj_timer_heap_poll(timer, &delay); #endif } if (state.err) { status = state.err; goto on_return; } PJ_LOG(3, ("", "...Done!")); PJ_LOG(3, ("", ".....Sent/recv: %d/%d bytes", state.sent, state.recv)); on_return: if (ssock && !state.err && !state.done) pj_ssl_sock_close(ssock); if (ioqueue) pj_ioqueue_destroy(ioqueue); if (timer) pj_timer_heap_destroy(timer); if (pool) pj_pool_release(pool); return status; }
/* Parse crypto attribute line */ static pj_status_t parse_attr_crypto(pj_pool_t *pool, const pjmedia_sdp_attr *attr, pjmedia_srtp_crypto *crypto, int *tag) { pj_str_t input; char *token; pj_str_t tmp; pj_status_t status; int itmp; pj_bzero(crypto, sizeof(*crypto)); pj_strdup_with_null(pool, &input, &attr->value); /* Tag */ token = strtok(input.ptr, " "); if (!token) { PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting tag")); return PJMEDIA_SDP_EINATTR; } *tag = atoi(token); if (*tag == 0) return PJMEDIA_SDP_EINATTR; /* Crypto-suite */ token = strtok(NULL, " "); if (!token) { PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting crypto suite")); return PJMEDIA_SDP_EINATTR; } crypto->name = pj_str(token); /* Key method */ token = strtok(NULL, ":"); if (!token) { PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key method")); return PJMEDIA_SDP_EINATTR; } if (pj_ansi_stricmp(token, "inline")) { PJ_LOG(4,(THIS_FILE, "Attribute crypto key method '%s' not supported!", token)); return PJMEDIA_SDP_EINATTR; } /* Key */ token = strtok(NULL, "| "); if (!token) { PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key")); return PJMEDIA_SDP_EINATTR; } tmp = pj_str(token); crypto->key.ptr = (char*) pj_pool_zalloc(pool, MAX_KEY_LEN); /* Decode key */ itmp = MAX_KEY_LEN; status = pj_base64_decode(&tmp, (pj_uint8_t*)crypto->key.ptr, &itmp); if (status != PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "Failed decoding crypto key from base64")); return status; } crypto->key.slen = itmp; return PJ_SUCCESS; }
/* main() * * If called with argument, treat argument as SIP URL to be called. * Otherwise wait for incoming calls. */ int main(int argc, char *argv[]) { struct pj_getopt_option long_options[] = { { "local-port", 1, 0, 'p' }, { "tcp", 0, 0, 't' }, { "ipv6", 0, 0, '6' }, { "help", 0, 0, 'h' } }; int c, option_index; pj_log_set_level(5); pj_init(); sip_af = pj_AF_INET(); pj_optind = 0; while ((c = pj_getopt_long(argc, argv, "p:t6h", long_options, &option_index)) != -1) { switch (c) { case 'p': sip_port = atoi(pj_optarg); break; case 't': sip_tcp = PJ_TRUE; break; case 'h': usage(); return 0; case '6': sip_af = pj_AF_INET6(); break; default: PJ_LOG(1,(THIS_FILE, "Argument \"%s\" is not valid. Use --help to see help", argv[pj_optind-1])); return -1; } } if (init_stack()) goto on_error; /* If URL is specified, then make call immediately. */ if (pj_optind != argc) { pj_sockaddr hostaddr; char hostip[PJ_INET6_ADDRSTRLEN+2]; char temp[80]; call_t *call; pj_str_t dst_uri = pj_str(argv[pj_optind]); pj_str_t local_uri; pjsip_dialog *dlg; pj_status_t status; pjsip_tx_data *tdata; if (pj_gethostip(sip_af, &hostaddr) != PJ_SUCCESS) { PJ_LOG(1,(THIS_FILE, "Unable to retrieve local host IP")); goto on_error; } pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2); pj_ansi_sprintf(temp, "<sip:sipecho@%s:%d>", hostip, sip_port); local_uri = pj_str(temp); call = &app.call[0]; status = pjsip_dlg_create_uac( pjsip_ua_instance(), &local_uri, /* local URI */ &local_uri, /* local Contact */ &dst_uri, /* remote URI */ &dst_uri, /* remote target */ &dlg); /* dialog */ if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create UAC dialog", status); return 1; } status = pjsip_inv_create_uac( dlg, NULL, 0, &call->inv); if (status != PJ_SUCCESS) goto on_error; call->inv->mod_data[mod_sipecho.id] = call; status = pjsip_inv_invite(call->inv, &tdata); if (status != PJ_SUCCESS) goto on_error; status = pjsip_inv_send_msg(call->inv, tdata); if (status != PJ_SUCCESS) goto on_error; puts("Press ENTER to quit..."); } else { puts("Ready for incoming calls. Press ENTER to quit..."); } for (;;) { char s[10]; printf("\nMenu:\n" " h Hangup all calls\n" " l %s message logging\n" " q Quit\n", (app.enable_msg_logging? "Disable" : "Enable")); if (fgets(s, sizeof(s), stdin) == NULL) continue; if (s[0]=='q') break; switch (s[0]) { case 'l': app.enable_msg_logging = !app.enable_msg_logging; break; case 'h': hangup_all(); break; } } destroy_stack(); puts("Bye bye.."); return 0; on_error: puts("An error has occurred. run a debugger.."); return 1; }
static pj_bool_t on_rx_request( pjsip_rx_data *rdata ) { pj_sockaddr hostaddr; char temp[80], hostip[PJ_INET6_ADDRSTRLEN]; pj_str_t local_uri; pjsip_dialog *dlg; pjsip_rdata_sdp_info *sdp_info; pjmedia_sdp_session *answer = NULL; pjsip_tx_data *tdata = NULL; call_t *call = NULL; unsigned i; pj_status_t status; PJ_LOG(3,(THIS_FILE, "RX %.*s from %s", (int)rdata->msg_info.msg->line.req.method.name.slen, rdata->msg_info.msg->line.req.method.name.ptr, rdata->pkt_info.src_name)); if (rdata->msg_info.msg->line.req.method.id == PJSIP_REGISTER_METHOD) { /* Let me be a registrar! */ pjsip_hdr hdr_list, *h; pjsip_msg *msg; int expires = -1; pj_list_init(&hdr_list); msg = rdata->msg_info.msg; h = (pjsip_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); if (h) { expires = ((pjsip_expires_hdr*)h)->ivalue; pj_list_push_back(&hdr_list, pjsip_hdr_clone(rdata->tp_info.pool, h)); PJ_LOG(3,(THIS_FILE, " Expires=%d", expires)); } if (expires != 0) { h = (pjsip_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL); if (h) pj_list_push_back(&hdr_list, pjsip_hdr_clone(rdata->tp_info.pool, h)); } pjsip_endpt_respond(app.sip_endpt, &mod_sipecho, rdata, 200, NULL, &hdr_list, NULL, NULL); return PJ_TRUE; } if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) { if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { pj_str_t reason = pj_str("Go away"); pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 400, &reason, NULL, NULL); } return PJ_TRUE; } sdp_info = pjsip_rdata_get_sdp_info(rdata); if (!sdp_info || !sdp_info->sdp) { pj_str_t reason = pj_str("Require valid offer"); pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 400, &reason, NULL, NULL); } for (i=0; i<MAX_CALLS; ++i) { if (app.call[i].inv == NULL) { call = &app.call[i]; break; } } if (i==MAX_CALLS) { pj_str_t reason = pj_str("We're full"); pjsip_endpt_respond_stateless( app.sip_endpt, rdata, PJSIP_SC_BUSY_HERE, &reason, NULL, NULL); return PJ_TRUE; } /* Generate Contact URI */ status = pj_gethostip(sip_af, &hostaddr); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to retrieve local host IP", status); return PJ_TRUE; } pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2); pj_ansi_sprintf(temp, "<sip:sipecho@%s:%d>", hostip, sip_port); local_uri = pj_str(temp); status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, &local_uri, &dlg); if (status == PJ_SUCCESS) answer = create_answer((int)(call-app.call), dlg->pool, sdp_info->sdp); if (status == PJ_SUCCESS) status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &call->inv); if (status == PJ_SUCCESS) status = pjsip_inv_initial_answer(call->inv, rdata, 100, NULL, NULL, &tdata); if (status == PJ_SUCCESS) status = pjsip_inv_send_msg(call->inv, tdata); if (status == PJ_SUCCESS) status = pjsip_inv_answer(call->inv, 180, NULL, NULL, &tdata); if (status == PJ_SUCCESS) status = pjsip_inv_send_msg(call->inv, tdata); if (status == PJ_SUCCESS) status = pjsip_inv_answer(call->inv, 200, NULL, NULL, &tdata); if (status == PJ_SUCCESS) status = pjsip_inv_send_msg(call->inv, tdata); if (status != PJ_SUCCESS) { pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 500, NULL, NULL, NULL); destroy_call(call); } else { call->inv->mod_data[mod_sipecho.id] = call; } return PJ_TRUE; }
/* * pjmedia_audiodev_strerror() */ PJ_DEF(pj_str_t) pjmedia_audiodev_strerror(pj_status_t statcode, char *buf, pj_size_t bufsize ) { pj_str_t errstr; #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) /* See if the error comes from Core Audio. */ #if PJMEDIA_AUDIO_DEV_HAS_COREAUDIO if (statcode >= PJMEDIA_AUDIODEV_COREAUDIO_ERRNO_START && statcode <= PJMEDIA_AUDIODEV_COREAUDIO_ERRNO_END) { int ca_err = PJMEDIA_AUDIODEV_COREAUDIO_ERRNO_START - statcode; PJ_UNUSED_ARG(ca_err); // TODO: create more helpful error messages errstr.ptr = buf; pj_strcpy2(&errstr, "Core audio error"); return errstr; } else #endif /* See if the error comes from PortAudio. */ #if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO if (statcode >= PJMEDIA_AUDIODEV_PORTAUDIO_ERRNO_START && statcode <= PJMEDIA_AUDIODEV_PORTAUDIO_ERRNO_END) { //int pa_err = statcode - PJMEDIA_ERRNO_FROM_PORTAUDIO(0); int pa_err = PJMEDIA_AUDIODEV_PORTAUDIO_ERRNO_START - statcode; pj_str_t msg; msg.ptr = (char*)Pa_GetErrorText(pa_err); msg.slen = pj_ansi_strlen(msg.ptr); errstr.ptr = buf; pj_strncpy_with_null(&errstr, &msg, bufsize); return errstr; } else #endif /* PJMEDIA_SOUND_IMPLEMENTATION */ /* See if the error comes from WMME */ #if PJMEDIA_AUDIO_DEV_HAS_WMME if ((statcode >= PJMEDIA_AUDIODEV_WMME_IN_ERROR_START && statcode < PJMEDIA_AUDIODEV_WMME_IN_ERROR_END) || (statcode >= PJMEDIA_AUDIODEV_WMME_OUT_ERROR_START && statcode < PJMEDIA_AUDIODEV_WMME_OUT_ERROR_END)) { MMRESULT native_err, mr; MMRESULT (WINAPI *waveGetErrText)(UINT mmrError, LPTSTR pszText, UINT cchText); PJ_DECL_UNICODE_TEMP_BUF(wbuf, 80) if (statcode >= PJMEDIA_AUDIODEV_WMME_IN_ERROR_START && statcode <= PJMEDIA_AUDIODEV_WMME_IN_ERROR_END) { native_err = statcode - PJMEDIA_AUDIODEV_WMME_IN_ERROR_START; waveGetErrText = &waveInGetErrorText; } else { native_err = statcode - PJMEDIA_AUDIODEV_WMME_OUT_ERROR_START; waveGetErrText = &waveOutGetErrorText; } #if PJ_NATIVE_STRING_IS_UNICODE mr = (*waveGetErrText)(native_err, wbuf, PJ_ARRAY_SIZE(wbuf)); if (mr == MMSYSERR_NOERROR) { int len = wcslen(wbuf); pj_unicode_to_ansi(wbuf, len, buf, bufsize); } #else mr = (*waveGetErrText)(native_err, buf, bufsize); #endif if (mr==MMSYSERR_NOERROR) { errstr.ptr = buf; errstr.slen = pj_ansi_strlen(buf); return errstr; } else { pj_ansi_snprintf(buf, bufsize, "MMSYSTEM native error %d", native_err); return pj_str(buf); } } else
/* * Handler to receive incoming MESSAGE */ static pj_bool_t im_on_rx_request(pjsip_rx_data *rdata) { pj_str_t from, to; pjsip_accept_hdr *accept_hdr; pjsip_msg *msg; msg = rdata->msg_info.msg; /* Only want to handle MESSAGE requests. */ if (pjsip_method_cmp(&msg->line.req.method, &pjsip_message_method) != 0) { return PJ_FALSE; } /* Should not have any transaction attached to rdata. */ PJ_ASSERT_RETURN(pjsip_rdata_get_tsx(rdata)==NULL, PJ_FALSE); /* Should not have any dialog attached to rdata. */ PJ_ASSERT_RETURN(pjsip_rdata_get_dlg(rdata)==NULL, PJ_FALSE); /* Check if we can accept the message. */ if (!pjsua_im_accept_pager(rdata, &accept_hdr)) { pjsip_hdr hdr_list; pj_list_init(&hdr_list); pj_list_push_back(&hdr_list, accept_hdr); pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE, NULL, &hdr_list, NULL); return PJ_TRUE; } /* Respond with 200 first, so that remote doesn't retransmit in case * the UI takes too long to process the message. */ pjsip_endpt_respond( pjsua_var.endpt, NULL, rdata, 200, NULL, NULL, NULL, NULL); /* For the source URI, we use Contact header if present, since * Contact header contains the port number information. If this is * not available, then use From header. */ from.ptr = (char*)pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE); from.slen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->msg_info.from->uri, from.ptr, PJSIP_MAX_URL_SIZE); if (from.slen < 1) from = pj_str("<--URI is too long-->"); /* Build the To text. */ to.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE); to.slen = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR, rdata->msg_info.to->uri, to.ptr, PJSIP_MAX_URL_SIZE); if (to.slen < 1) to = pj_str("<--URI is too long-->"); /* Process pager. */ pjsua_im_process_pager(-1, &from, &to, rdata); /* Done. */ return PJ_TRUE; }
/*! \brief Apply handler for transports */ static int transport_apply(const struct ast_sorcery *sorcery, void *obj) { struct ast_sip_transport *transport = obj; const char *transport_id = ast_sorcery_object_get_id(obj); RAII_VAR(struct ao2_container *, states, transport_states, states_cleanup); RAII_VAR(struct internal_state *, temp_state, NULL, ao2_cleanup); RAII_VAR(struct internal_state *, perm_state, NULL, ao2_cleanup); RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy); pj_status_t res = -1; int i; #define BIND_TRIES 3 #define BIND_DELAY_US 100000 if (!states) { return -1; } /* * transport_apply gets called for EVERY retrieval of a transport when using realtime. * We need to prevent multiple threads from trying to mess with underlying transports * at the same time. The container is the only thing we have to lock on. */ ao2_wrlock(states); temp_state = internal_state_alloc(transport); if (!temp_state) { ast_log(LOG_ERROR, "Transport '%s' failed to allocate memory\n", transport_id); return -1; } perm_state = find_internal_state_by_transport(transport); if (perm_state) { ast_sorcery_diff(sorcery, perm_state->transport, transport, &changes); if (!changes && !has_state_changed(perm_state->state, temp_state->state)) { /* In case someone is using the deprecated fields, reset them */ transport->state = perm_state->state; copy_state_to_transport(transport); ao2_replace(perm_state->transport, transport); return 0; } if (!transport->allow_reload) { if (!perm_state->change_detected) { perm_state->change_detected = 1; ast_log(LOG_WARNING, "Transport '%s' is not reloadable, maintaining previous values\n", transport_id); } /* In case someone is using the deprecated fields, reset them */ transport->state = perm_state->state; copy_state_to_transport(transport); ao2_replace(perm_state->transport, transport); return 0; } } if (temp_state->state->host.addr.sa_family != PJ_AF_INET && temp_state->state->host.addr.sa_family != PJ_AF_INET6) { ast_log(LOG_ERROR, "Transport '%s' could not be started as binding not specified\n", transport_id); return -1; } /* Set default port if not present */ if (!pj_sockaddr_get_port(&temp_state->state->host)) { pj_sockaddr_set_port(&temp_state->state->host, (transport->type == AST_TRANSPORT_TLS) ? 5061 : 5060); } /* Now that we know what address family we can set up a dnsmgr refresh for the external media address if present */ if (!ast_strlen_zero(transport->external_signaling_address)) { if (temp_state->state->host.addr.sa_family == pj_AF_INET()) { temp_state->state->external_address.ss.ss_family = AF_INET; } else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) { temp_state->state->external_address.ss.ss_family = AF_INET6; } else { ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external signaling address\n", transport_id); return -1; } if (ast_dnsmgr_lookup(transport->external_signaling_address, &temp_state->state->external_address, &temp_state->state->external_address_refresher, NULL) < 0) { ast_log(LOG_ERROR, "Could not create dnsmgr for external signaling address on '%s'\n", transport_id); return -1; } } if (transport->type == AST_TRANSPORT_UDP) { for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) { if (perm_state && perm_state->state && perm_state->state->transport) { pjsip_udp_transport_pause(perm_state->state->transport, PJSIP_UDP_TRANSPORT_DESTROY_SOCKET); usleep(BIND_DELAY_US); } if (temp_state->state->host.addr.sa_family == pj_AF_INET()) { res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(), &temp_state->state->host.ipv4, NULL, transport->async_operations, &temp_state->state->transport); } else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) { res = pjsip_udp_transport_start6(ast_sip_get_pjsip_endpoint(), &temp_state->state->host.ipv6, NULL, transport->async_operations, &temp_state->state->transport); } } if (res == PJ_SUCCESS && (transport->tos || transport->cos)) { pj_sock_t sock; pj_qos_params qos_params; sock = pjsip_udp_transport_get_socket(temp_state->state->transport); pj_sock_get_qos_params(sock, &qos_params); set_qos(transport, &qos_params); pj_sock_set_qos_params(sock, &qos_params); } } else if (transport->type == AST_TRANSPORT_TCP) { pjsip_tcp_transport_cfg cfg; pjsip_tcp_transport_cfg_default(&cfg, temp_state->state->host.addr.sa_family); cfg.bind_addr = temp_state->state->host; cfg.async_cnt = transport->async_operations; set_qos(transport, &cfg.qos_params); for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) { if (perm_state && perm_state->state && perm_state->state->factory && perm_state->state->factory->destroy) { perm_state->state->factory->destroy(perm_state->state->factory); usleep(BIND_DELAY_US); } res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg, &temp_state->state->factory); } } else if (transport->type == AST_TRANSPORT_TLS) { if (transport->async_operations > 1 && ast_compare_versions(pj_get_version(), "2.5.0") < 0) { ast_log(LOG_ERROR, "Transport: %s: When protocol=tls and pjproject version < 2.5.0, async_operations can't be > 1\n", ast_sorcery_object_get_id(obj)); return -1; } temp_state->state->tls.password = pj_str((char*)transport->password); set_qos(transport, &temp_state->state->tls.qos_params); for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) { if (perm_state && perm_state->state && perm_state->state->factory && perm_state->state->factory->destroy) { perm_state->state->factory->destroy(perm_state->state->factory); usleep(BIND_DELAY_US); } res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &temp_state->state->tls, &temp_state->state->host, NULL, transport->async_operations, &temp_state->state->factory); } } else if ((transport->type == AST_TRANSPORT_WS) || (transport->type == AST_TRANSPORT_WSS)) { if (transport->cos || transport->tos) { ast_log(LOG_WARNING, "TOS and COS values ignored for websocket transport\n"); } res = PJ_SUCCESS; } if (res != PJ_SUCCESS) { char msg[PJ_ERR_MSG_SIZE]; pj_strerror(res, msg, sizeof(msg)); ast_log(LOG_ERROR, "Transport '%s' could not be started: %s\n", ast_sorcery_object_get_id(obj), msg); return -1; } copy_state_to_transport(transport); if (perm_state) { ao2_unlink_flags(states, perm_state, OBJ_NOLOCK); } ao2_link_flags(states, temp_state, OBJ_NOLOCK); return 0; }
/* * Send instant messaging outside dialog, using the specified account for * route set and authentication. */ PJ_DEF(pj_status_t) pjsua_im_send( pjsua_acc_id acc_id, const pj_str_t *to, const pj_str_t *mime_type, const pj_str_t *content, const pjsua_msg_data *msg_data, void *user_data) { pjsip_tx_data *tdata; const pj_str_t mime_text_plain = pj_str("text/plain"); pjsip_media_type media_type; pjsua_im_data *im_data; pjsua_acc *acc; pj_status_t status; /* To and message body must be specified. */ PJ_ASSERT_RETURN(to && content, PJ_EINVAL); acc = &pjsua_var.acc[acc_id]; /* Create request. */ status = pjsip_endpt_create_request(pjsua_var.endpt, &pjsip_message_method, (msg_data && msg_data->target_uri.slen? &msg_data->target_uri: to), &acc->cfg.id, to, NULL, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create request", status); return status; } /* If account is locked to specific transport, then set transport to * the request. */ if (acc->cfg.transport_id != PJSUA_INVALID_ID) { pjsip_tpselector tp_sel; pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel); pjsip_tx_data_set_transport(tdata, &tp_sel); } /* Add accept header. */ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)pjsua_im_create_accept(tdata->pool)); /* Create suitable Contact header unless a Contact header has been * set in the account. */ /* Ticket #1632: According to RFC 3428: * MESSAGE requests do not initiate dialogs. * User Agents MUST NOT insert Contact header fields into MESSAGE requests */ /* if (acc->contact.slen) { contact = acc->contact; } else { status = pjsua_acc_create_uac_contact(tdata->pool, &contact, acc_id, to); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to generate Contact header", status); pjsip_tx_data_dec_ref(tdata); return status; } } pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool, &STR_CONTACT, &contact)); */ /* Create IM data to keep message details and give it back to * application on the callback */ im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data); im_data->acc_id = acc_id; im_data->call_id = PJSUA_INVALID_ID; pj_strdup_with_null(tdata->pool, &im_data->to, to); pj_strdup_with_null(tdata->pool, &im_data->body, content); im_data->user_data = user_data; /* Set default media type if none is specified */ if (mime_type == NULL) { mime_type = &mime_text_plain; } /* Parse MIME type */ pjsua_parse_media_type(tdata->pool, mime_type, &media_type); /* Add message body */ tdata->msg->body = pjsip_msg_body_create( tdata->pool, &media_type.type, &media_type.subtype, &im_data->body); if (tdata->msg->body == NULL) { pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM); pjsip_tx_data_dec_ref(tdata); return PJ_ENOMEM; } /* Add additional headers etc. */ pjsua_process_msg_data(tdata, msg_data); /* Add route set */ pjsua_set_msg_route_set(tdata, &acc->route_set); /* If via_addr is set, use this address for the Via header. */ if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) { tdata->via_addr = acc->via_addr; tdata->via_tp = acc->via_tp; } /* Send request (statefully) */ status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1, im_data, &im_callback); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send request", status); return status; } return PJ_SUCCESS; }
static pj_status_t create_relay(void) { pj_turn_sock_cb rel_cb; pj_stun_auth_cred cred; pj_str_t srv; pj_status_t status; if (g.relay) { PJ_LOG(1,(THIS_FILE, "Relay already created")); return -1; } /* Create DNS resolver if configured */ if (o.nameserver) { pj_str_t ns = pj_str(o.nameserver); status = pj_dns_resolver_create(&g.cp.factory, "resolver", 0, g.stun_config.timer_heap, g.stun_config.ioqueue, &g.resolver); if (status != PJ_SUCCESS) { PJ_LOG(1,(THIS_FILE, "Error creating resolver (err=%d)", status)); return status; } status = pj_dns_resolver_set_ns(g.resolver, 1, &ns, NULL); if (status != PJ_SUCCESS) { PJ_LOG(1,(THIS_FILE, "Error configuring nameserver (err=%d)", status)); return status; } } pj_bzero(&rel_cb, sizeof(rel_cb)); rel_cb.on_rx_data = &turn_on_rx_data; rel_cb.on_state = &turn_on_state; CHECK( pj_turn_sock_create(&g.stun_config, pj_AF_INET(), (o.use_tcp? PJ_TURN_TP_TCP : PJ_TURN_TP_UDP), &rel_cb, 0, NULL, &g.relay) ); if (o.user_name) { pj_bzero(&cred, sizeof(cred)); cred.type = PJ_STUN_AUTH_CRED_STATIC; cred.data.static_cred.realm = pj_str(o.realm); cred.data.static_cred.username = pj_str(o.user_name); cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN; cred.data.static_cred.data = pj_str(o.password); //cred.data.static_cred.nonce = pj_str(o.nonce); } else { PJ_LOG(2,(THIS_FILE, "Warning: no credential is set")); } srv = pj_str(o.srv_addr); CHECK(pj_turn_sock_alloc(g.relay, /* the relay */ &srv, /* srv addr */ (o.srv_port?atoi(o.srv_port):PJ_STUN_PORT),/* def port */ g.resolver, /* resolver */ (o.user_name?&cred:NULL), /* credential */ NULL) /* alloc param */ ); return PJ_SUCCESS; }
/* * Callback on media state changed event. * The action may connect the call to sound device, to file, or * to loop the call. */ static void on_call_media_state(pjsua_call_id call_id) { pjsua_call_info call_info; pjsua_call_get_info(call_id, &call_info); /* Connect ports appropriately when media status is ACTIVE or REMOTE HOLD, * otherwise we should NOT connect the ports. */ if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE || call_info.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD) { pj_bool_t connect_sound = PJ_TRUE; /* Loopback sound, if desired */ if (app_config.auto_loop) { pjsua_conf_connect(call_info.conf_slot, call_info.conf_slot); connect_sound = PJ_FALSE; } /* Automatically record conversation, if desired */ if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) { pjsua_conf_connect(call_info.conf_slot, app_config.rec_port); } /* Stream a file, if desired */ if ((app_config.auto_play || app_config.auto_play_hangup) && app_config.wav_port != PJSUA_INVALID_ID) { pjsua_conf_connect(app_config.wav_port, call_info.conf_slot); connect_sound = PJ_FALSE; } /* Put call in conference with other calls, if desired */ if (app_config.auto_conf) { pjsua_call_id call_ids[PJSUA_MAX_CALLS]; unsigned call_cnt=PJ_ARRAY_SIZE(call_ids); unsigned i; /* Get all calls, and establish media connection between * this call and other calls. */ pjsua_enum_calls(call_ids, &call_cnt); for (i=0; i<call_cnt; ++i) { if (call_ids[i] == call_id) continue; if (!pjsua_call_has_media(call_ids[i])) continue; pjsua_conf_connect(call_info.conf_slot, pjsua_call_get_conf_port(call_ids[i])); pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]), call_info.conf_slot); /* Automatically record conversation, if desired */ if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) { pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]), app_config.rec_port); } } /* Also connect call to local sound device */ connect_sound = PJ_TRUE; } /* Otherwise connect to sound device */ if (connect_sound) { pjsua_conf_connect(call_info.conf_slot, 0); pjsua_conf_connect(0, call_info.conf_slot); /* Automatically record conversation, if desired */ if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) { pjsua_conf_connect(call_info.conf_slot, app_config.rec_port); pjsua_conf_connect(0, app_config.rec_port); } } } PJ_LOG(3,(THIS_FILE, "Media for call %d is active", call_id)); /* Handle media status */ switch (call_info.media_status) { case PJSUA_CALL_MEDIA_ACTIVE: PJ_LOG(3,(THIS_FILE, "Media for call %d is active", call_id)); break; case PJSUA_CALL_MEDIA_LOCAL_HOLD: PJ_LOG(3,(THIS_FILE, "Media for call %d is suspended (hold) by local", call_id)); // call on call hold handler cb_callholdconf(call_id); break; case PJSUA_CALL_MEDIA_REMOTE_HOLD: PJ_LOG(3,(THIS_FILE, "Media for call %d is suspended (hold) by remote", call_id)); break; case PJSUA_CALL_MEDIA_ERROR: PJ_LOG(3,(THIS_FILE, "Media has reported error, disconnecting call")); { pj_str_t reason = pj_str("ICE negotiation failed"); pjsua_call_hangup(call_id, 500, &reason, NULL); } break; case PJSUA_CALL_MEDIA_NONE: PJ_LOG(3,(THIS_FILE, "Media for call %d is inactive", call_id)); break; default: pj_assert(!"Unhandled media status"); break; } }
/** * Is call using a secure RTP method (SRTP/ZRTP) */ PJ_DECL(pj_str_t) call_secure_info(pjsua_call_id call_id) { pjsua_call *call; pjsip_dialog *dlg; pj_status_t status; unsigned i; pjmedia_transport_info tp_info; pj_str_t result = pj_str(""); PJ_LOG(3, (THIS_FILE, "Get call secure info...")); PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, result); /* Use PJSUA_LOCK() instead of acquire_call(): * https://trac.pjsip.org/repos/ticket/1371 */ PJSUA_LOCK(); if (pjsua_call_has_media(call_id)) { call = &pjsua_var.calls[call_id]; for (i = 0; i < call->med_cnt; ++i) { pjsua_call_media *call_med = &call->media[i]; PJ_LOG(4, (THIS_FILE, "Get secure for media type %d", call_med->type)); /* Get and ICE SRTP status */ if (call_med->tp && call_med->type == PJMEDIA_TYPE_AUDIO) { pjmedia_transport_info tp_info; pjmedia_transport_info_init(&tp_info); pjmedia_transport_get_info(call_med->tp, &tp_info); if (tp_info.specific_info_cnt > 0) { unsigned j; for (j = 0; j < tp_info.specific_info_cnt; ++j) { if (tp_info.spc_info[j].type == PJMEDIA_TRANSPORT_TYPE_SRTP) { pjmedia_srtp_info *srtp_info = (pjmedia_srtp_info*) tp_info.spc_info[j].buffer; if (srtp_info->active) { result = pj_str("SRTP"); break; } } #if defined(PJMEDIA_HAS_ZRTP) && PJMEDIA_HAS_ZRTP!=0 else if (tp_info.spc_info[j].type == PJMEDIA_TRANSPORT_TYPE_ZRTP) { pjmedia_zrtp_info *zrtp_info = (pjmedia_zrtp_info*) tp_info.spc_info[j].buffer; // if(zrtp_info->active){ result = jzrtp_getInfo(call_med->tp); break; // } } #endif } } } } } PJSUA_UNLOCK(); return result; }
/***************************************************************************** * 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; }
*/PJ_DECL(pj_str_t) call_dump(pjsua_call_id call_id, pj_bool_t with_media, const char *indent) { char some_buf[1024 * 3]; pjsua_call_dump(call_id, with_media, some_buf, sizeof(some_buf), indent); return pj_str(some_buf); }
/***************************************************************************** * 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 an SRTP media transport. */ PJ_DEF(pj_status_t) pjmedia_transport_srtp_create( pjmedia_endpt *endpt, pjmedia_transport *tp, const pjmedia_srtp_setting *opt, pjmedia_transport **p_tp) { pj_pool_t *pool; transport_srtp *srtp; pj_status_t status; unsigned i; PJ_ASSERT_RETURN(endpt && tp && p_tp, PJ_EINVAL); /* Check crypto availability */ if (opt && opt->crypto_count == 0 && opt->use == PJMEDIA_SRTP_MANDATORY) return PJMEDIA_SRTP_ESDPREQCRYPTO; /* Check crypto */ if (opt && opt->use != PJMEDIA_SRTP_DISABLED) { for (i=0; i < opt->crypto_count; ++i) { int cs_idx = get_crypto_idx(&opt->crypto[i].name); /* check crypto name */ if (cs_idx == -1) return PJMEDIA_SRTP_ENOTSUPCRYPTO; /* check key length */ if (opt->crypto[i].key.slen && opt->crypto[i].key.slen < (pj_ssize_t)crypto_suites[cs_idx].cipher_key_len) return PJMEDIA_SRTP_EINKEYLEN; } } /* Init libsrtp. */ status = pjmedia_srtp_init_lib(); if (status != PJ_SUCCESS) return status; pool = pjmedia_endpt_create_pool(endpt, "srtp%p", 1000, 1000); srtp = PJ_POOL_ZALLOC_T(pool, transport_srtp); srtp->pool = pool; srtp->session_inited = PJ_FALSE; srtp->bypass_srtp = PJ_FALSE; srtp->probation_cnt = PROBATION_CNT_INIT; if (opt) { srtp->setting = *opt; if (opt->use == PJMEDIA_SRTP_DISABLED) srtp->setting.crypto_count = 0; for (i=0; i < srtp->setting.crypto_count; ++i) { int cs_idx = get_crypto_idx(&opt->crypto[i].name); pj_str_t tmp_key = opt->crypto[i].key; /* re-set crypto */ srtp->setting.crypto[i].name = pj_str(crypto_suites[cs_idx].name); /* cut key length */ if (tmp_key.slen) tmp_key.slen = crypto_suites[cs_idx].cipher_key_len; pj_strdup(pool, &srtp->setting.crypto[i].key, &tmp_key); } } else { pjmedia_srtp_setting_default(&srtp->setting); } status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &srtp->mutex); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } /* Initialize base pjmedia_transport */ pj_memcpy(srtp->base.name, pool->obj_name, PJ_MAX_OBJ_NAME); if (tp) srtp->base.type = tp->type; else srtp->base.type = PJMEDIA_TRANSPORT_TYPE_UDP; srtp->base.op = &transport_srtp_op; /* Set underlying transport */ srtp->member_tp = tp; /* Initialize peer's SRTP usage mode. */ srtp->peer_use = srtp->setting.use; /* Done */ *p_tp = &srtp->base; return PJ_SUCCESS; }
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'; } }
/* * Convert address string with numbers and dots to binary IP address. */ PJ_DEF(pj_in_addr) pj_inet_addr2(const char *cp) { pj_str_t str = pj_str((char*)cp); return pj_inet_addr(&str); }
pj_status_t create_test_server(pj_stun_config *stun_cfg, pj_uint32_t flags, const char *domain, test_server **p_test_srv) { pj_pool_t *pool; test_server *test_srv; pj_sockaddr hostip; char strbuf[100]; pj_status_t status; PJ_ASSERT_RETURN(stun_cfg && domain && p_test_srv, PJ_EINVAL); status = pj_gethostip(pj_AF_INET(), &hostip); if (status != PJ_SUCCESS) return status; pool = pj_pool_create(mem, THIS_FILE, 512, 512, NULL); test_srv = (test_server*) PJ_POOL_ZALLOC_T(pool, test_server); test_srv->pool = pool; test_srv->flags = flags; test_srv->stun_cfg = stun_cfg; pj_strdup2(pool, &test_srv->domain, domain); test_srv->username = pj_str(TURN_USERNAME); test_srv->passwd = pj_str(TURN_PASSWD); pj_ioqueue_op_key_init(&test_srv->send_key, sizeof(test_srv->send_key)); if (flags & CREATE_DNS_SERVER) { status = pj_dns_server_create(mem, test_srv->stun_cfg->ioqueue, pj_AF_INET(), DNS_SERVER_PORT, 0, &test_srv->dns_server); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); return status; } /* Add DNS A record for the domain, for fallback */ if (flags & CREATE_A_RECORD_FOR_DOMAIN) { pj_dns_parsed_rr rr; pj_str_t res_name; pj_in_addr ip_addr; pj_strdup2(pool, &res_name, domain); ip_addr = hostip.ipv4.sin_addr; pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); } } if (flags & CREATE_STUN_SERVER) { pj_activesock_cb stun_sock_cb; pj_sockaddr bound_addr; pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb)); stun_sock_cb.on_data_recvfrom = &stun_on_data_recvfrom; pj_sockaddr_in_init(&bound_addr.ipv4, NULL, STUN_SERVER_PORT); status = pj_activesock_create_udp(pool, &bound_addr, NULL, test_srv->stun_cfg->ioqueue, &stun_sock_cb, test_srv, &test_srv->stun_sock, NULL); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); return status; } status = pj_activesock_start_recvfrom(test_srv->stun_sock, pool, MAX_STUN_PKT, 0); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); return status; } if (test_srv->dns_server && (flags & CREATE_STUN_SERVER_DNS_SRV)) { pj_str_t res_name, target; pj_dns_parsed_rr rr; pj_in_addr ip_addr; /* Add DNS entries: * _stun._udp.domain 60 IN SRV 0 0 PORT stun.domain. * stun.domain IN A 127.0.0.1 */ pj_ansi_snprintf(strbuf, sizeof(strbuf), "_stun._udp.%s", domain); pj_strdup2(pool, &res_name, strbuf); pj_ansi_snprintf(strbuf, sizeof(strbuf), "stun.%s", domain); pj_strdup2(pool, &target, strbuf); pj_dns_init_srv_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, 0, 0, STUN_SERVER_PORT, &target); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); res_name = target; ip_addr = hostip.ipv4.sin_addr; pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); } } if (flags & CREATE_TURN_SERVER) { pj_activesock_cb turn_sock_cb; pj_sockaddr bound_addr; pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb)); turn_sock_cb.on_data_recvfrom = &turn_on_data_recvfrom; pj_sockaddr_in_init(&bound_addr.ipv4, NULL, TURN_SERVER_PORT); status = pj_activesock_create_udp(pool, &bound_addr, NULL, test_srv->stun_cfg->ioqueue, &turn_sock_cb, test_srv, &test_srv->turn_sock, NULL); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); return status; } status = pj_activesock_start_recvfrom(test_srv->turn_sock, pool, MAX_STUN_PKT, 0); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); return status; } if (test_srv->dns_server && (flags & CREATE_TURN_SERVER_DNS_SRV)) { pj_str_t res_name, target; pj_dns_parsed_rr rr; pj_in_addr ip_addr; /* Add DNS entries: * _turn._udp.domain 60 IN SRV 0 0 PORT turn.domain. * turn.domain IN A 127.0.0.1 */ pj_ansi_snprintf(strbuf, sizeof(strbuf), "_turn._udp.%s", domain); pj_strdup2(pool, &res_name, strbuf); pj_ansi_snprintf(strbuf, sizeof(strbuf), "turn.%s", domain); pj_strdup2(pool, &target, strbuf); pj_dns_init_srv_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, 0, 0, TURN_SERVER_PORT, &target); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); res_name = target; ip_addr = hostip.ipv4.sin_addr; pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); } } *p_test_srv = test_srv; return PJ_SUCCESS; }
/* * GET request scenario 1: using on_response() and on_data_read() * Server replies with content-length. Application cancels the * request upon receiving the response, then start it again. */ int http_client_test1() { pj_str_t url; pj_http_req_callback hcb; pj_http_req_param param; char urlbuf[80]; pj_bzero(&hcb, sizeof(hcb)); hcb.on_complete = &on_complete; hcb.on_data_read = &on_data_read; hcb.on_response = &on_response; pj_http_req_param_default(¶m); /* Create pool, timer, and ioqueue */ pool = pj_pool_create(mem, NULL, 8192, 4096, NULL); if (pj_timer_heap_create(pool, 16, &timer_heap)) return -31; if (pj_ioqueue_create(pool, 16, &ioqueue)) return -32; #ifdef USE_LOCAL_SERVER thread_quit = PJ_FALSE; g_server.action = ACTION_REPLY; g_server.send_content_length = PJ_TRUE; g_server.data_size = 2970; g_server.buf_size = 1024; sstatus = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &g_server.sock); if (sstatus != PJ_SUCCESS) return -41; pj_sockaddr_in_init(&addr, NULL, 0); sstatus = pj_sock_bind(g_server.sock, &addr, sizeof(addr)); if (sstatus != PJ_SUCCESS) return -43; { pj_sockaddr_in addr; int addr_len = sizeof(addr); sstatus = pj_sock_getsockname(g_server.sock, &addr, &addr_len); if (sstatus != PJ_SUCCESS) return -44; g_server.port = pj_sockaddr_in_get_port(&addr); pj_ansi_snprintf(urlbuf, sizeof(urlbuf), "http://127.0.0.1:%d/about-us/", g_server.port); url = pj_str(urlbuf); } sstatus = pj_sock_listen(g_server.sock, 8); if (sstatus != PJ_SUCCESS) return -45; sstatus = pj_thread_create(pool, NULL, &server_thread, &g_server, 0, 0, &g_server.thread); if (sstatus != PJ_SUCCESS) return -47; #else pj_cstr(&url, "http://www.teluu.com/about-us/"); #endif if (pj_http_req_create(pool, &url, timer_heap, ioqueue, ¶m, &hcb, &http_req)) return -33; test_cancel = PJ_TRUE; if (pj_http_req_start(http_req)) return -35; while (pj_http_req_is_running(http_req)) { pj_time_val delay = {0, 50}; pj_ioqueue_poll(ioqueue, &delay); pj_timer_heap_poll(timer_heap, NULL); } if (pj_http_req_start(http_req)) return -37; while (pj_http_req_is_running(http_req)) { pj_time_val delay = {0, 50}; pj_ioqueue_poll(ioqueue, &delay); pj_timer_heap_poll(timer_heap, NULL); } #ifdef USE_LOCAL_SERVER thread_quit = PJ_TRUE; pj_thread_join(g_server.thread); pj_sock_close(g_server.sock); #endif pj_http_req_destroy(http_req); pj_ioqueue_destroy(ioqueue); pj_timer_heap_destroy(timer_heap); pj_pool_release(pool); return PJ_SUCCESS; }
/* * Invalid response scenario: when server returns no MAPPED-ADDRESS or * XOR-MAPPED-ADDRESS attribute. */ static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err) { struct stun_srv *srv; struct stun_client *client; pj_str_t srv_addr; pj_time_val timeout, t; int i, ret = 0; pj_status_t status; PJ_LOG(3,(THIS_FILE, " missing attribute test [%d]", destroy_on_err)); status = create_client(cfg, &client, destroy_on_err); if (status != PJ_SUCCESS) return -110; status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN, &srv); if (status != PJ_SUCCESS) { destroy_client(client); return -120; } srv_addr = pj_str("127.0.0.1"); status = pj_stun_sock_start(client->sock, &srv_addr, pj_ntohs(srv->addr.ipv4.sin_port), NULL); if (status != PJ_SUCCESS) { destroy_server(srv); destroy_client(client); return -130; } /* Wait until on_status() callback is called with the failure */ pj_gettimeofday(&timeout); timeout.sec += 60; do { handle_events(cfg, 100); pj_gettimeofday(&t); } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout)); /* Check that callback with correct operation is called */ if (client->last_op != PJ_STUN_SOCK_BINDING_OP) { PJ_LOG(3,(THIS_FILE, " error: expecting Binding operation status")); ret = -140; goto on_return; } if (client->last_status != PJNATH_ESTUNNOMAPPEDADDR) { PJ_LOG(3,(THIS_FILE, " error: expecting PJNATH_ESTUNNOMAPPEDADDR")); ret = -150; goto on_return; } /* Check that client doesn't receive anything */ if (client->on_rx_data_cnt != 0) { PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything")); ret = -170; goto on_return; } on_return: destroy_server(srv); destroy_client(client); for (i=0; i<7; ++i) handle_events(cfg, 50); return ret; }