From cfde494dc3631d80e1190318c942931a3cbd50b3 Mon Sep 17 00:00:00 2001 From: naf Date: Tue, 19 Mar 2024 03:02:07 -0500 Subject: [PATCH 001/491] Add missing openssl SECLEVEL=0 support (#3890) Previous SECLEVEL support allowed for levels 1-5. However, openssl defines levels 0-5. [1] Recent openssl versions (3.0+) have moved previous popular ciphers/key lengths (i.e. RSA1024withSHA1) into level 0, so it is now a reasonable choice to use. Add support for level 0. [1] https://www.openssl.org/docs/man3.2/man3/SSL_CTX_set_security_level.html --- pjlib/src/pj/ssl_sock_ossl.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c index 5253fec053..3716f4f616 100644 --- a/pjlib/src/pj/ssl_sock_ossl.c +++ b/pjlib/src/pj/ssl_sock_ossl.c @@ -483,11 +483,12 @@ static pj_str_t ssl_strerror(pj_status_t status, */ static const struct ssl_ciphers_t ADDITIONAL_CIPHERS[] = { {0xFF000000, "DEFAULT"}, - {0xFF000001, "@SECLEVEL=1"}, - {0xFF000002, "@SECLEVEL=2"}, - {0xFF000003, "@SECLEVEL=3"}, - {0xFF000004, "@SECLEVEL=4"}, - {0xFF000005, "@SECLEVEL=5"} + {0xFF000001, "@SECLEVEL=0"}, + {0xFF000002, "@SECLEVEL=1"}, + {0xFF000003, "@SECLEVEL=2"}, + {0xFF000004, "@SECLEVEL=3"}, + {0xFF000005, "@SECLEVEL=4"}, + {0xFF000006, "@SECLEVEL=5"} }; static const unsigned int ADDITIONAL_CIPHER_COUNT = sizeof (ADDITIONAL_CIPHERS) / sizeof (ADDITIONAL_CIPHERS[0]); From a7c4d83807ceb25c5067c1dcb5d863becc7986c3 Mon Sep 17 00:00:00 2001 From: Goodicus <15110766+goodicus@users.noreply.github.com> Date: Thu, 21 Mar 2024 11:58:24 +0200 Subject: [PATCH 002/491] Enable Late Offer Answer Mode (LOAM) feature in the pjsua (#3869) --- pjsip-apps/src/pjsua/pjsua_app.c | 3 +++ pjsip-apps/src/pjsua/pjsua_app_cli.c | 34 +++++++++++++++++++++++ pjsip-apps/src/pjsua/pjsua_app_common.h | 1 + pjsip-apps/src/pjsua/pjsua_app_config.c | 1 + pjsip-apps/src/pjsua/pjsua_app_legacy.c | 36 ++++++++++++++++++++++++- 5 files changed, 74 insertions(+), 1 deletion(-) diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c index f072b39083..3c819b34ec 100644 --- a/pjsip-apps/src/pjsua/pjsua_app.c +++ b/pjsip-apps/src/pjsua/pjsua_app.c @@ -1998,6 +1998,9 @@ static pj_status_t app_init(void) pjsua_call_setting_default(&call_opt); call_opt.aud_cnt = app_config.aud_cnt; call_opt.vid_cnt = app_config.vid.vid_cnt; + if (app_config.enable_loam) { + call_opt.flag |= PJSUA_CALL_NO_SDP_OFFER; + } #if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0 /* Wipe out TLS key settings in transport configs */ diff --git a/pjsip-apps/src/pjsua/pjsua_app_cli.c b/pjsip-apps/src/pjsua/pjsua_app_cli.c index bf2237eda6..44849230d5 100644 --- a/pjsip-apps/src/pjsua/pjsua_app_cli.c +++ b/pjsip-apps/src/pjsua/pjsua_app_cli.c @@ -38,6 +38,7 @@ #define CMD_QUIT 110 #define CMD_RESTART 120 #define CMD_HANDLE_IP_CHANGE 130 +#define CMD_TOGGLE_SDP_OFFER 140 /* call level 2 command */ #define CMD_CALL_NEW ((CMD_CALL*10)+1) @@ -2086,6 +2087,19 @@ static pj_status_t cmd_show_current_call(pj_cli_cmd_val *cval) return PJ_SUCCESS; } +static pj_status_t cmd_toggle_call_sdp_offer(pj_cli_cmd_val* cval) +{ + char out_str[64]; + app_config.enable_loam = !app_config.enable_loam; + + pj_ansi_snprintf(out_str, sizeof(out_str), + "Subsequent calls and UPDATEs will contain SDP offer: %s\n", + app_config.enable_loam ? "No" : "Yes"); + pj_cli_sess_write_msg(cval->sess, out_str, pj_ansi_strlen(out_str)); + + return PJ_SUCCESS; +} + /* Call handler */ pj_status_t cmd_call_handler(pj_cli_cmd_val *cval) { @@ -2094,6 +2108,14 @@ pj_status_t cmd_call_handler(pj_cli_cmd_val *cval) CHECK_PJSUA_RUNNING(); + /* Update call setting */ + pjsua_call_setting_default(&call_opt); + call_opt.aud_cnt = app_config.aud_cnt; + call_opt.vid_cnt = app_config.vid.vid_cnt; + if (app_config.enable_loam) { + call_opt.flag |= PJSUA_CALL_NO_SDP_OFFER; + } + switch(cmd_id) { case CMD_CALL_NEW: status = cmd_make_single_call(cval); @@ -3221,12 +3243,17 @@ static pj_status_t add_other_command(pj_cli_t *c) char* ip_change_command = ""; + char* toggle_sdp_offer_command = + ""; + pj_status_t status; pj_str_t sleep_xml = pj_str(sleep_command); pj_str_t network_xml = pj_str(network_command); pj_str_t shutdown_xml = pj_str(shutdown_command); pj_str_t restart_xml = pj_str(restart_command); pj_str_t ip_change_xml = pj_str(ip_change_command); + pj_str_t toggle_sdp_offer_xml = pj_str(toggle_sdp_offer_command); status = pj_cli_add_cmd_from_xml(c, NULL, &sleep_xml, cmd_sleep_handler, @@ -3257,6 +3284,13 @@ static pj_status_t add_other_command(pj_cli_t *c) status = pj_cli_add_cmd_from_xml(c, NULL, &ip_change_xml, cmd_ip_change_handler, NULL, NULL); + if (status != PJ_SUCCESS) + return status; + + status = pj_cli_add_cmd_from_xml(c, NULL, + &toggle_sdp_offer_xml, + cmd_toggle_call_sdp_offer, + NULL, NULL); return status; } diff --git a/pjsip-apps/src/pjsua/pjsua_app_common.h b/pjsip-apps/src/pjsua/pjsua_app_common.h index 06c81718d5..f3359a3792 100644 --- a/pjsip-apps/src/pjsua/pjsua_app_common.h +++ b/pjsip-apps/src/pjsua/pjsua_app_common.h @@ -78,6 +78,7 @@ typedef struct pjsua_app_config pj_bool_t ipv6; pj_bool_t enable_qos; pj_bool_t no_mci; + pj_bool_t enable_loam; pj_bool_t no_tcp; pj_bool_t no_udp; pj_bool_t use_tls; diff --git a/pjsip-apps/src/pjsua/pjsua_app_config.c b/pjsip-apps/src/pjsua/pjsua_app_config.c index 6168f7789a..76825a1bf1 100644 --- a/pjsip-apps/src/pjsua/pjsua_app_config.c +++ b/pjsip-apps/src/pjsua/pjsua_app_config.c @@ -1988,6 +1988,7 @@ int write_settings(pjsua_app_config *config, char *buf, pj_size_t max) if (config->no_mci) { pj_strcat2(&cfg, "--no-mci\n"); } + /* UDP Transport. */ pj_ansi_snprintf(line, sizeof(line), "--local-port %d\n", config->udp_cfg.port); diff --git a/pjsip-apps/src/pjsua/pjsua_app_legacy.c b/pjsip-apps/src/pjsua/pjsua_app_legacy.c index fbc011bf8a..d174f0ecec 100644 --- a/pjsip-apps/src/pjsua/pjsua_app_legacy.c +++ b/pjsip-apps/src/pjsua/pjsua_app_legacy.c @@ -236,7 +236,7 @@ static void keystroke_help() puts("| a Answer call | i Send IM | !a Modify accnt. |"); puts("| h Hangup call (ha=all) | s Subscribe presence | rr (Re-)register |"); puts("| H Hold call | u Unsubscribe presence | ru Unregister |"); - puts("| | D Subscribe dlg event | |"); + puts("| o Toggle call SDP offer | D Subscribe dlg event | |"); puts("| | Du Unsub dlg event | |"); puts("| v re-inVite (release hold) | t Toggle online status | > Cycle next ac.|"); puts("| U send UPDATE | T Set online status | < Cycle prev ac.|"); @@ -734,6 +734,9 @@ static void ui_make_new_call() pjsua_msg_data_init(&msg_data_); TEST_MULTIPART(&msg_data_); + if (app_config.enable_loam) { + call_opt.flag |= PJSUA_CALL_NO_SDP_OFFER; + } pjsua_call_make_call(current_acc, &tmp, &call_opt, NULL, &msg_data_, ¤t_call); @@ -773,6 +776,10 @@ static void ui_make_multi_call() tmp = pj_str(result.uri_result); } + if (app_config.enable_loam) { + call_opt.flag |= PJSUA_CALL_NO_SDP_OFFER; + } + for (i=0; i Date: Sun, 24 Mar 2024 23:28:36 -0400 Subject: [PATCH 003/491] Fix warnings for 32-bit compiler and misc fixes. (#3896) --- pjmedia/src/pjmedia-codec/speex_codec.c | 2 +- pjmedia/src/pjmedia/clock_thread.c | 3 ++- pjnath/src/pjnath/ice_session.c | 4 ++-- pjsip/src/pjsua-lib/pjsua_core.c | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pjmedia/src/pjmedia-codec/speex_codec.c b/pjmedia/src/pjmedia-codec/speex_codec.c index ed451f1fbe..eaa782cf97 100644 --- a/pjmedia/src/pjmedia-codec/speex_codec.c +++ b/pjmedia/src/pjmedia-codec/speex_codec.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -34,6 +33,7 @@ */ #if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0 +#include #define THIS_FILE "speex_codec.c" diff --git a/pjmedia/src/pjmedia/clock_thread.c b/pjmedia/src/pjmedia/clock_thread.c index ff85485274..d5cd964266 100644 --- a/pjmedia/src/pjmedia/clock_thread.c +++ b/pjmedia/src/pjmedia/clock_thread.c @@ -79,7 +79,8 @@ pjmedia_clock_src_get_time_msec( const pjmedia_clock_src *clocksrc ) { pj_timestamp ts; - pjmedia_clock_src_get_current_timestamp(clocksrc, &ts); + if (pjmedia_clock_src_get_current_timestamp(clocksrc, &ts) != PJ_SUCCESS) + return 0; #if PJ_HAS_INT64 if (ts.u64 > PJ_UINT64(0x3FFFFFFFFFFFFF)) diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c index 408d10d2ba..8afe4d181c 100644 --- a/pjnath/src/pjnath/ice_session.c +++ b/pjnath/src/pjnath/ice_session.c @@ -1677,8 +1677,8 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, } } - LOG5((ice->obj_name, "Check %ld is successful%s", - GET_CHECK_ID(&ice->clist, check), + LOG5((ice->obj_name, "Check %d is successful%s", + (int)GET_CHECK_ID(&ice->clist, check), (check->nominated ? " and nominated" : ""))); /* On the first valid pair, we call the callback, if present */ diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 901777752b..10fe57d9b5 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -485,10 +485,10 @@ static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) * transport layer. So don't try to access tp_info when the module * has lower priority than transport layer. */ - PJ_LOG(4,(THIS_FILE, "TX %ld bytes %s to %s %s:\n" + PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s %s:\n" "%.*s\n" "--end msg--", - (tdata->buf.cur - tdata->buf.start), + (int)(tdata->buf.cur - tdata->buf.start), pjsip_tx_data_get_info(tdata), tdata->tp_info.transport->type_name, pj_addr_str_print(&input_str, From 427a2b3ab8f8c64659d4b5ad3250219ffc37788b Mon Sep 17 00:00:00 2001 From: Santiago De la Cruz <51337247+xhit@users.noreply.github.com> Date: Mon, 25 Mar 2024 23:52:13 -0400 Subject: [PATCH 004/491] Add some missing unlocks (#3893) --- pjlib-util/src/pjlib-util/resolver.c | 8 ++++---- pjlib/src/pj/ioqueue_winnt.c | 1 + pjlib/src/pj/pool_caching.c | 1 + pjnath/src/pjnath/turn_session.c | 4 +++- pjsip/src/pjsip/sip_transport.c | 4 +++- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/pjlib-util/src/pjlib-util/resolver.c b/pjlib-util/src/pjlib-util/resolver.c index 0fd0cf7efa..1ec8daace4 100644 --- a/pjlib-util/src/pjlib-util/resolver.c +++ b/pjlib-util/src/pjlib-util/resolver.c @@ -1840,15 +1840,15 @@ PJ_DEF(pj_status_t) pj_dns_resolver_add_entry( pj_dns_resolver *resolver, pj_bzero(&key, sizeof(struct res_key)); if (pkt->hdr.anscount) { /* Make sure name is not too long. */ - PJ_ASSERT_RETURN(pkt->ans[0].name.slen < PJ_MAX_HOSTNAME, - PJ_ENAMETOOLONG); + PJ_ASSERT_ON_FAIL(pkt->ans[0].name.slen < PJ_MAX_HOSTNAME, + { pj_grp_lock_release(resolver->grp_lock); return PJ_ENAMETOOLONG; }); init_res_key(&key, pkt->ans[0].type, &pkt->ans[0].name); } else { /* Make sure name is not too long. */ - PJ_ASSERT_RETURN(pkt->q[0].name.slen < PJ_MAX_HOSTNAME, - PJ_ENAMETOOLONG); + PJ_ASSERT_ON_FAIL(pkt->q[0].name.slen < PJ_MAX_HOSTNAME, + { pj_grp_lock_release(resolver->grp_lock); return PJ_ENAMETOOLONG; }); init_res_key(&key, pkt->q[0].type, &pkt->q[0].name); } diff --git a/pjlib/src/pj/ioqueue_winnt.c b/pjlib/src/pj/ioqueue_winnt.c index d2a45dbcc8..11b6573bc7 100644 --- a/pjlib/src/pj/ioqueue_winnt.c +++ b/pjlib/src/pj/ioqueue_winnt.c @@ -495,6 +495,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioqueue ) } #endif + pj_lock_release(ioqueue->lock); if (ioqueue->auto_delete_lock) pj_lock_destroy(ioqueue->lock); diff --git a/pjlib/src/pj/pool_caching.c b/pjlib/src/pj/pool_caching.c index 666ab1473a..e917d1f29b 100644 --- a/pjlib/src/pj/pool_caching.c +++ b/pjlib/src/pj/pool_caching.c @@ -220,6 +220,7 @@ static void cpool_release_pool( pj_pool_factory *pf, pj_pool_t *pool) #if PJ_SAFE_POOL /* Make sure pool is still in our used list */ if (pj_list_find_node(&cp->used_list, pool) != pool) { + pj_lock_release(cp->lock); pj_assert(!"Attempt to destroy pool that has been destroyed before"); return; } diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c index 1ed0a40564..e85e971f80 100644 --- a/pjnath/src/pjnath/turn_session.c +++ b/pjnath/src/pjnath/turn_session.c @@ -664,7 +664,9 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess, unsigned i, cnt; /* Default port must be specified */ - PJ_ASSERT_RETURN(default_port>0 && default_port<65536, PJ_EINVAL); + PJ_ASSERT_ON_FAIL(default_port>0 && default_port<65536, + {status=PJ_EINVAL; goto on_return;}); + sess->default_port = (pj_uint16_t)default_port; cnt = PJ_TURN_MAX_DNS_SRV_CNT; diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index 706250d7ec..3b30046fec 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -1283,8 +1283,10 @@ PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr, /* Allocate new entry for the freelist. */ for (; i < PJSIP_TRANSPORT_ENTRY_ALLOC_CNT; ++i) { tp_add = PJ_POOL_ZALLOC_T(mgr->pool, transport); - if (!tp_add) + if (!tp_add){ + pj_lock_release(mgr->lock); return PJ_ENOMEM; + } pj_list_init(tp_add); pj_list_push_back(&mgr->tp_entry_freelist, tp_add); } From c0de1a42243a9655d178166226fdfc18edd60b90 Mon Sep 17 00:00:00 2001 From: sauwming Date: Tue, 26 Mar 2024 11:52:54 +0800 Subject: [PATCH 005/491] Prevent race condition in DTLS media stop (#3901) --- pjmedia/src/pjmedia/transport_srtp_dtls.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pjmedia/src/pjmedia/transport_srtp_dtls.c b/pjmedia/src/pjmedia/transport_srtp_dtls.c index f23635007d..69c837e088 100644 --- a/pjmedia/src/pjmedia/transport_srtp_dtls.c +++ b/pjmedia/src/pjmedia/transport_srtp_dtls.c @@ -1890,8 +1890,10 @@ static pj_status_t dtls_media_stop(pjmedia_transport *tp) PJ_LOG(2,(ds->base.name, "dtls_media_stop()")); #endif + DTLS_LOCK(ds); dtls_media_stop_channel(ds, RTP_CHANNEL); dtls_media_stop_channel(ds, RTCP_CHANNEL); + DTLS_UNLOCK(ds); ds->setup = DTLS_SETUP_UNKNOWN; ds->use_ice = PJ_FALSE; From 478aeb95e93bbe3c8d25a80e08c10c4b5e3ea199 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 26 Mar 2024 15:20:44 +0700 Subject: [PATCH 006/491] Fix data race reported by ThreadSanitizer in caching pool (#3897) --- pjlib/include/pj/pool.h | 4 ++++ pjlib/src/pj/pool_caching.c | 8 ++++++-- pjnath/src/pjturn-srv/main.c | 5 +++-- pjsip-apps/src/samples/pjsip-perf.c | 5 +++-- pjsip/src/test/test.c | 7 ++++--- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/pjlib/include/pj/pool.h b/pjlib/include/pj/pool.h index 9030a6ec7b..969a486ea9 100644 --- a/pjlib/include/pj/pool.h +++ b/pjlib/include/pj/pool.h @@ -849,12 +849,16 @@ struct pj_caching_pool /** * Total size of memory currently used by application. + * + * This field is deprecated. */ pj_size_t used_size; /** * The maximum size of memory used by application throughout the life * of the caching pool. + * + * This field is deprecated. */ pj_size_t peak_used_size; diff --git a/pjlib/src/pj/pool_caching.c b/pjlib/src/pj/pool_caching.c index e917d1f29b..38511a764f 100644 --- a/pjlib/src/pj/pool_caching.c +++ b/pjlib/src/pj/pool_caching.c @@ -75,8 +75,12 @@ PJ_DEF(void) pj_caching_pool_init( pj_caching_pool *cp, cp->factory.create_pool = &cpool_create_pool; cp->factory.release_pool = &cpool_release_pool; cp->factory.dump_status = &cpool_dump_status; - cp->factory.on_block_alloc = &cpool_on_block_alloc; - cp->factory.on_block_free = &cpool_on_block_free; + + /* Deprecated, these callbacks are only used for updating cp.used_size & + * cp.peak_used_size which are no longer used. + */ + //cp->factory.on_block_alloc = &cpool_on_block_alloc; + //cp->factory.on_block_free = &cpool_on_block_free; pool = pj_pool_create_on_buf("cachingpool", cp->pool_buf, sizeof(cp->pool_buf)); status = pj_lock_create_simple_mutex(pool, "cachingpool", &cp->lock); diff --git a/pjnath/src/pjturn-srv/main.c b/pjnath/src/pjturn-srv/main.c index 33ff4877e5..d8898f9ce2 100644 --- a/pjnath/src/pjturn-srv/main.c +++ b/pjnath/src/pjturn-srv/main.c @@ -49,8 +49,9 @@ static void dump_status(pj_turn_srv *srv) } printf("Worker threads : %d\n", srv->core.thread_cnt); - printf("Total mem usage: %u.%03uMB\n", (unsigned)(g_cp.used_size / 1000000), - (unsigned)((g_cp.used_size % 1000000)/1000)); + /* Field used_size is deprecated by #3897 */ + //printf("Total mem usage: %u.%03uMB\n", (unsigned)(g_cp.used_size / 1000000), + // (unsigned)((g_cp.used_size % 1000000)/1000)); printf("UDP port range : %u %u %u (next/min/max)\n", srv->ports.next_udp, srv->ports.min_udp, srv->ports.max_udp); printf("TCP port range : %u %u %u (next/min/max)\n", srv->ports.next_tcp, diff --git a/pjsip-apps/src/samples/pjsip-perf.c b/pjsip-apps/src/samples/pjsip-perf.c index 9fdcfc201e..196c49ac5b 100644 --- a/pjsip-apps/src/samples/pjsip-perf.c +++ b/pjsip-apps/src/samples/pjsip-perf.c @@ -900,8 +900,9 @@ static void destroy_app() if (app.pool) { pj_pool_release(app.pool); app.pool = NULL; - PJ_LOG(3,(THIS_FILE, "Peak memory size: %luMB", - app.cp.peak_used_size / 1000000)); + /* Field peak_used_size is deprecated by #3897 */ + //PJ_LOG(3,(THIS_FILE, "Peak memory size: %luMB", + // app.cp.peak_used_size / 1000000)); pj_caching_pool_destroy(&app.cp); } diff --git a/pjsip/src/test/test.c b/pjsip/src/test/test.c index 1730fb3366..37c4dabec0 100644 --- a/pjsip/src/test/test.c +++ b/pjsip/src/test/test.c @@ -502,9 +502,10 @@ int test_main(char *testlist) pj_log_set_level(4); /* Dumping memory pool usage */ - PJ_LOG(3,(THIS_FILE, "Peak memory size=%lu MB", - (unsigned long) - (caching_pool.peak_used_size / 1000000))); + /* Field peak_used_size is deprecated by #3897 */ + //PJ_LOG(3,(THIS_FILE, "Peak memory size=%lu MB", + // (unsigned long) + // (caching_pool.peak_used_size / 1000000))); pjsip_endpt_destroy(endpt); pj_caching_pool_destroy(&caching_pool); From 51e52062e111ecfa9c68b7dab5fdfa64097c868a Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 29 Mar 2024 10:05:02 +0800 Subject: [PATCH 007/491] Fixed Metal renderer memory leak (#3909) --- pjmedia/src/pjmedia-codec/vid_toolbox.m | 23 +++++++++++++---------- pjmedia/src/pjmedia-videodev/metal_dev.m | 7 +++++++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/pjmedia/src/pjmedia-codec/vid_toolbox.m b/pjmedia/src/pjmedia-codec/vid_toolbox.m index f12050f43d..01b1eceed6 100644 --- a/pjmedia/src/pjmedia-codec/vid_toolbox.m +++ b/pjmedia/src/pjmedia-codec/vid_toolbox.m @@ -554,17 +554,20 @@ static OSStatus create_encoder(vtool_codec_data *vtool_data) ret = VTSessionCopySupportedPropertyDictionary(vtool_data->enc, &supported_prop); - if (ret == noErr && - CFDictionaryContainsKey(supported_prop, + if (ret == noErr) { + if (CFDictionaryContainsKey(supported_prop, kVTCompressionPropertyKey_MaxH264SliceBytes)) - { - /* kVTCompressionPropertyKey_MaxH264SliceBytes is not yet supported - * by Apple. We leave it here for possible future enhancements. - SET_PROPERTY(vtool_data->enc, - kVTCompressionPropertyKey_MaxH264SliceBytes, - // param->enc_mtu - NAL_HEADER_ADD_0X30BYTES - (__bridge CFTypeRef)@(param->enc_mtu - 50)); - */ + { + /* kVTCompressionPropertyKey_MaxH264SliceBytes is not yet supported + * by Apple. We leave it here for possible future enhancements. + SET_PROPERTY(vtool_data->enc, + kVTCompressionPropertyKey_MaxH264SliceBytes, + // param->enc_mtu - NAL_HEADER_ADD_0X30BYTES + (__bridge CFTypeRef)@(param->enc_mtu - 50)); + */ + } + + CFRelease(supported_prop); } VTCompressionSessionPrepareToEncodeFrames(vtool_data->enc); diff --git a/pjmedia/src/pjmedia-videodev/metal_dev.m b/pjmedia/src/pjmedia-videodev/metal_dev.m index 3118687739..f12bfa8d99 100644 --- a/pjmedia/src/pjmedia-videodev/metal_dev.m +++ b/pjmedia/src/pjmedia-videodev/metal_dev.m @@ -428,6 +428,11 @@ - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView textureCoordBuffer = [device newBufferWithBytes:textureCoordinates length:sizeof(textureCoordinates) options:MTLResourceStorageModeShared]; + + [pQuadPipelineStateDescriptor release]; + [fragmentProgram release]; + [vertexProgram release]; + [shaderLibrary release]; } return self; @@ -509,6 +514,8 @@ - (void)update_image [commandBuffer presentDrawable:drawable]; [commandBuffer commit]; + [texture release]; + stream->is_rendering = PJ_FALSE; } From 86b7dd48b5e7b0abf7f55b4c2e81759df6d72a6e Mon Sep 17 00:00:00 2001 From: sauwming Date: Mon, 1 Apr 2024 10:52:31 +0800 Subject: [PATCH 008/491] Fixed DTLS clock stoppage race (#3905) --- pjmedia/src/pjmedia/transport_srtp_dtls.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pjmedia/src/pjmedia/transport_srtp_dtls.c b/pjmedia/src/pjmedia/transport_srtp_dtls.c index 69c837e088..f57ba1bc57 100644 --- a/pjmedia/src/pjmedia/transport_srtp_dtls.c +++ b/pjmedia/src/pjmedia/transport_srtp_dtls.c @@ -829,8 +829,6 @@ static pj_status_t ssl_flush_wbio(dtls_srtp *ds, unsigned idx) PJ_LOG(2,(ds->base.name, "DTLS-SRTP negotiation for %s completed!", CHANNEL_TO_STRING(idx))); - DTLS_UNLOCK(ds); - /* Stop the retransmission clock. Note that the clock may not be stopped * if this function is called from clock thread context. We'll try again * later in socket context. @@ -838,6 +836,8 @@ static pj_status_t ssl_flush_wbio(dtls_srtp *ds, unsigned idx) if (ds->clock[idx]) pjmedia_clock_stop(ds->clock[idx]); + DTLS_UNLOCK(ds); + /* Get SRTP key material */ status = ssl_get_srtp_material(ds, idx); if (status != PJ_SUCCESS) { From e7e7f28f16b914d45cb3ca39c47dd40ece1726cc Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 3 Apr 2024 18:04:51 +0800 Subject: [PATCH 009/491] Improve IP address change IPv4 <-> IPv6 (#3910) --- pjsip-apps/src/pjsua/pjsua_app_legacy.c | 2 -- pjsip/include/pjsua-lib/pjsua.h | 12 ++++++++++ pjsip/include/pjsua2/account.hpp | 12 ++++++++++ pjsip/src/pjsip/sip_dialog.c | 9 ++++++- pjsip/src/pjsip/sip_util.c | 24 ++++++++++++++++++- pjsip/src/pjsua-lib/pjsua_acc.c | 32 +++++++++++++++++++++---- pjsip/src/pjsua2/account.cpp | 2 ++ 7 files changed, 84 insertions(+), 9 deletions(-) diff --git a/pjsip-apps/src/pjsua/pjsua_app_legacy.c b/pjsip-apps/src/pjsua/pjsua_app_legacy.c index d174f0ecec..afa64439f1 100644 --- a/pjsip-apps/src/pjsua/pjsua_app_legacy.c +++ b/pjsip-apps/src/pjsua/pjsua_app_legacy.c @@ -1848,8 +1848,6 @@ static void ui_handle_ip_change() status = pjsua_handle_ip_change(¶m); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "IP change failed", status); - } else { - PJ_LOG(3,(THIS_FILE, "IP change succeeded")); } } diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 0fcdb486d6..47ed776df6 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -4552,6 +4552,18 @@ typedef struct pjsua_acc_config */ pj_bool_t register_on_acc_add; + /** + * Specify whether account modification with pjsua_acc_modify() should + * automatically update registration if necessary, for example if + * account credentials change. + * + * Disable this when immediate registration is not desirable, such as + * during IP address change. + * + * Default: PJ_FALSE. + */ + pj_bool_t disable_reg_on_modify; + /** * Specify account configuration specific to IP address change used when * calling #pjsua_handle_ip_change(). diff --git a/pjsip/include/pjsua2/account.hpp b/pjsip/include/pjsua2/account.hpp index 4ed74255d9..a39f1b634b 100644 --- a/pjsip/include/pjsua2/account.hpp +++ b/pjsip/include/pjsua2/account.hpp @@ -62,6 +62,18 @@ struct AccountRegConfig : public PersistentObject */ bool registerOnAdd; + /** + * Specify whether account modification with Account::modify() should + * automatically update registration if necessary, for example if + * account credentials change. + * + * Disable this when immediate registration is not desirable, such as + * during IP address change. + * + * Default: false. + */ + bool disableRegOnModify; + /** * The optional custom SIP headers to be put in the registration * request. diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c index 1be1f0704c..2c40106a66 100644 --- a/pjsip/src/pjsip/sip_dialog.c +++ b/pjsip/src/pjsip/sip_dialog.c @@ -474,6 +474,8 @@ pj_status_t create_uas_dialog( pjsip_user_agent *ua, if (rdata->tp_info.transport->dir == PJSIP_TP_DIR_OUTGOING) { pj_strdup(dlg->pool, &dlg->initial_dest, &rdata->tp_info.transport->remote_name.host); + PJ_LOG(5, (THIS_FILE, "Saving initial dest %.*s", + (int)dlg->initial_dest.slen, dlg->initial_dest.ptr)); } /* Init remote's contact from Contact header. @@ -1222,8 +1224,11 @@ static pj_status_t dlg_create_request_throw( pjsip_dialog *dlg, /* Copy the initial destination host to tdata. This information can be * used later by transport for transport selection. */ - if (dlg->initial_dest.slen) + if (dlg->initial_dest.slen) { pj_strdup(tdata->pool, &tdata->dest_info.name, &dlg->initial_dest); + PJ_LOG(5, (THIS_FILE, "Setting initial dest %.*s", + (int)dlg->initial_dest.slen, dlg->initial_dest.ptr)); + } /* Done. */ *p_tdata = tdata; @@ -1878,6 +1883,8 @@ static void dlg_update_routeset(pjsip_dialog *dlg, const pjsip_rx_data *rdata) { pj_strdup(dlg->pool, &dlg->initial_dest, &rdata->tp_info.transport->remote_name.host); + PJ_LOG(5, (THIS_FILE, "Saving initial dest %.*s", + (int)dlg->initial_dest.slen, dlg->initial_dest.ptr)); } else { /* Reset the stored remote name if the transport is a server * transport. diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c index 4c5b3265c7..1167093166 100644 --- a/pjsip/src/pjsip/sip_util.c +++ b/pjsip/src/pjsip/sip_util.c @@ -1409,7 +1409,7 @@ stateless_send_resolver_callback( pj_status_t status, if (tdata->tp_sel.type == PJSIP_TPSELECTOR_IP_VER) { PJ_LOG(5, (THIS_FILE, "Resorting target addresses based on " "%s preference", - tdata->tp_sel.u.ip_ver == PJSIP_TPSELECTOR_PREFER_IPV4? + tdata->tp_sel.u.ip_ver <= PJSIP_TPSELECTOR_PREFER_IPV4? "IPv4": "IPv6")); if (tdata->tp_sel.u.ip_ver == PJSIP_TPSELECTOR_PREFER_IPV4) resort_address(&tdata->dest_info.addr, pj_AF_INET()); @@ -1463,6 +1463,28 @@ PJ_DEF(pj_status_t) pjsip_endpt_send_request_stateless(pjsip_endpoint *endpt, if (!tdata->dest_info.name.slen) { pj_strdup(tdata->pool, &tdata->dest_info.name, &dest_info.addr.host); + } else { + /* Check if: + * - User configures transport to use a specific IP version + * - The IP version doesn't match with destination info + */ + if (tdata->tp_sel.type == PJSIP_TPSELECTOR_IP_VER && + ((tdata->tp_sel.u.ip_ver == PJSIP_TPSELECTOR_USE_IPV4_ONLY && + (dest_info.type & PJSIP_TRANSPORT_IPV6) != 0) || + (tdata->tp_sel.u.ip_ver == PJSIP_TPSELECTOR_USE_IPV6_ONLY && + (dest_info.type & PJSIP_TRANSPORT_IPV6) == 0))) + { + PJ_LOG(5, (THIS_FILE, "Using initial dest %.*s", + (int)tdata->dest_info.name.slen, + tdata->dest_info.name.ptr)); + pj_strdup(tdata->pool, &dest_info.addr.host, + &tdata->dest_info.name); + if (tdata->tp_sel.u.ip_ver == PJSIP_TPSELECTOR_USE_IPV4_ONLY) { + dest_info.type &= ~PJSIP_TRANSPORT_IPV6; + } else { + dest_info.type |= PJSIP_TRANSPORT_IPV6; + } + } } pjsip_endpt_resolve( endpt, tdata->pool, &dest_info, stateless_data, diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index 40d10765a1..6efdb143a0 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -1026,6 +1026,14 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id, acc->cfg.ipv6_sip_use = cfg->ipv6_sip_use; update_reg = PJ_TRUE; unreg_first = PJ_TRUE; + + /* Set client registration's transport based on acc's config. */ + if (acc->regc) { + pjsip_tpselector tp_sel; + + pjsua_init_tpselector(acc->index, &tp_sel); + pjsip_regc_set_transport(acc->regc, &tp_sel); + } } /* User data */ @@ -1446,7 +1454,7 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id, acc->cfg.call_hold_type = cfg->call_hold_type; /* Unregister first */ - if (unreg_first) { + if (unreg_first && !cfg->disable_reg_on_modify) { if (acc->regc) { status = pjsua_acc_set_registration(acc->index, PJ_FALSE); if (status != PJ_SUCCESS) { @@ -1466,7 +1474,7 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id, } /* Update registration */ - if (update_reg) { + if (update_reg && !cfg->disable_reg_on_modify) { /* If accounts has registration enabled, start registration */ if (acc->cfg.reg_uri.slen) { status = pjsua_acc_set_registration(acc->index, PJ_TRUE); @@ -4335,6 +4343,8 @@ pj_status_t pjsua_acc_handle_call_on_ip_change(pjsua_acc *acc) { for (i = 0; i < pjsua_var.ua_cfg.max_calls; ++i) { pjsua_call_info call_info; + pjsua_call *call; + pjsip_dialog *dlg = NULL; if (!pjsua_call_is_active(i) || pjsua_var.calls[i].acc_id != acc->index || @@ -4343,6 +4353,21 @@ pj_status_t pjsua_acc_handle_call_on_ip_change(pjsua_acc *acc) continue; } + status = acquire_call("call_tpsel_on_ip_change()", + i, &call, &dlg); + if (status == PJ_SUCCESS) { + pjsip_tpselector tp_sel; + + /* Set dialog's transport based on acc's config. */ + pjsua_init_tpselector(call->acc_id, &tp_sel); + pjsip_dlg_set_transport(dlg, &tp_sel); + + pjsip_dlg_dec_lock(dlg); + } else { + PJ_LOG(4, (THIS_FILE, "Failed to update call %d's transport " + "selector", i)); + } + if ((acc->cfg.ip_change_cfg.hangup_calls) && (call_info.state >= PJSIP_INV_STATE_EARLY)) { @@ -4398,9 +4423,6 @@ pj_status_t pjsua_acc_handle_call_on_ip_change(pjsua_acc *acc) /* Check if remote support SIP UPDATE method */ if (use_update) { - pjsua_call *call; - pjsip_dialog *dlg = NULL; - PJ_LOG(5, (THIS_FILE, "Call #%d: IP change is configured " "to using UPDATE", i)); diff --git a/pjsip/src/pjsua2/account.cpp b/pjsip/src/pjsua2/account.cpp index cfa829ae43..4db9214fe7 100644 --- a/pjsip/src/pjsua2/account.cpp +++ b/pjsip/src/pjsua2/account.cpp @@ -575,6 +575,7 @@ void AccountConfig::toPj(pjsua_acc_config &ret) const // AccountRegConfig ret.reg_uri = str2Pj(regConfig.registrarUri); ret.register_on_acc_add = regConfig.registerOnAdd; + ret.disable_reg_on_modify = regConfig.disableRegOnModify; ret.reg_timeout = regConfig.timeoutSec; ret.reg_retry_interval = regConfig.retryIntervalSec; ret.reg_first_retry_interval= regConfig.firstRetryIntervalSec; @@ -734,6 +735,7 @@ void AccountConfig::fromPj(const pjsua_acc_config &prm, // AccountRegConfig regConfig.registrarUri = pj2Str(prm.reg_uri); regConfig.registerOnAdd = (prm.register_on_acc_add != 0); + regConfig.disableRegOnModify= (prm.disable_reg_on_modify != 0); regConfig.timeoutSec = prm.reg_timeout; regConfig.retryIntervalSec = prm.reg_retry_interval; regConfig.firstRetryIntervalSec = prm.reg_first_retry_interval; From 6d114d71d5ed79a69cfbe345fff9fb1f88afbed2 Mon Sep 17 00:00:00 2001 From: Amilcar Ubiera Date: Fri, 5 Apr 2024 02:05:36 -0400 Subject: [PATCH 010/491] =?UTF-8?q?pjsua=5Facc:=20Fix=20warnings=20for=20c?= =?UTF-8?q?omparison=20between=20=E2=80=98pjsua=5Fnat64=5Fopt=E2=80=99=20a?= =?UTF-8?q?nd=20=E2=80=98enum=20pjsua=5Fipv6=5Fuse=E2=80=99=20(#3915)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pjsip/src/pjsua-lib/pjsua_acc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index 6efdb143a0..c113467b42 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -465,7 +465,7 @@ PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg, #if !PJ_HAS_IPV6 PJ_ASSERT_RETURN(cfg->ipv6_sip_use == PJSUA_IPV6_DISABLED, PJ_EINVAL); PJ_ASSERT_RETURN(cfg->ipv6_media_use == PJSUA_IPV6_DISABLED, PJ_EINVAL); - PJ_ASSERT_RETURN(cfg->nat64_opt == PJSUA_IPV6_DISABLED, PJ_EINVAL); + PJ_ASSERT_RETURN(cfg->nat64_opt == PJSUA_NAT64_DISABLED, PJ_EINVAL); #endif /* Must have a transport */ @@ -854,7 +854,7 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id, #if !PJ_HAS_IPV6 PJ_ASSERT_RETURN(cfg->ipv6_sip_use == PJSUA_IPV6_DISABLED, PJ_EINVAL); PJ_ASSERT_RETURN(cfg->ipv6_media_use == PJSUA_IPV6_DISABLED, PJ_EINVAL); - PJ_ASSERT_RETURN(cfg->nat64_opt == PJSUA_IPV6_DISABLED, PJ_EINVAL); + PJ_ASSERT_RETURN(cfg->nat64_opt == PJSUA_NAT64_DISABLED, PJ_EINVAL); #endif PJ_LOG(4,(THIS_FILE, "Modifying account %d", acc_id)); From 12d0468cb67f651436b30479170669f523d7653f Mon Sep 17 00:00:00 2001 From: Amilcar Ubiera Date: Sun, 14 Apr 2024 22:25:30 -0400 Subject: [PATCH 011/491] Fix to ext_fmts accessed out of stack scope. (#3916) --- pjsip/src/pjsua-lib/pjsua_aud.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index 1b7756dfb1..591be2b83b 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -288,6 +288,9 @@ pj_status_t pjsua_aud_subsys_init() unsigned opt; pjmedia_audio_codec_config codec_cfg; pj_status_t status; +#if PJMEDIA_HAS_PASSTHROUGH_CODECS + pjmedia_format ext_fmts[32]; +#endif /* To suppress warning about unused var when all codecs are disabled */ PJ_UNUSED_ARG(codec_id); @@ -305,7 +308,6 @@ pj_status_t pjsua_aud_subsys_init() { unsigned aud_idx; unsigned ext_fmt_cnt = 0; - pjmedia_format ext_fmts[32]; /* List extended formats supported by audio devices */ for (aud_idx = 0; aud_idx < pjmedia_aud_dev_count(); ++aud_idx) { From 72d885d5d786c23463224211a7e9e4a3e7d6bd9e Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 17 Apr 2024 11:21:39 +0800 Subject: [PATCH 012/491] Add check in siprtp sample app for inactive audio media (#3927) --- pjsip-apps/src/samples/siprtp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pjsip-apps/src/samples/siprtp.c b/pjsip-apps/src/samples/siprtp.c index bc8dc8b3bf..46edc5ad46 100644 --- a/pjsip-apps/src/samples/siprtp.c +++ b/pjsip-apps/src/samples/siprtp.c @@ -1420,6 +1420,11 @@ static void call_on_media_update( pjsip_inv_session *inv, pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp); pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp); + if (local_sdp->media[0]->desc.port == 0) { + PJ_LOG(3, (THIS_FILE, "Audio media inactive")); + return; + } + status = pjmedia_stream_info_from_sdp(&audio->si, inv->pool, app.med_endpt, local_sdp, remote_sdp, 0); if (status != PJ_SUCCESS) { From c5bc3d1ef5d7983dc5e0720f2abe03d25d9eca65 Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 17 Apr 2024 11:21:57 +0800 Subject: [PATCH 013/491] Add function to initialize MediaFormat audio & video (#3925) --- pjsip-apps/src/samples/pjsua2_demo.cpp | 6 +---- pjsip/include/pjsua2/media.hpp | 34 ++++++++++++++++++++--- pjsip/src/pjsua2/media.cpp | 37 ++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 9 deletions(-) diff --git a/pjsip-apps/src/samples/pjsua2_demo.cpp b/pjsip-apps/src/samples/pjsua2_demo.cpp index 1c8af786e8..1ce27cecd8 100644 --- a/pjsip-apps/src/samples/pjsua2_demo.cpp +++ b/pjsip-apps/src/samples/pjsua2_demo.cpp @@ -205,11 +205,7 @@ void MyCall::onCallMediaState(OnCallMediaStateParam &prm) med_port = new MyAudioMediaPort(); MediaFormatAudio fmt; - fmt.type = PJMEDIA_TYPE_AUDIO; - fmt.clockRate = 16000; - fmt.channelCount = 1; - fmt.bitsPerSample = 16; - fmt.frameTimeUsec = 20000; + fmt.init(PJMEDIA_FORMAT_PCM, 16000, 1, 20000, 16); med_port->createPort("med_port", fmt); diff --git a/pjsip/include/pjsua2/media.hpp b/pjsip/include/pjsua2/media.hpp index acafaca67a..ef9edde9d0 100644 --- a/pjsip/include/pjsua2/media.hpp +++ b/pjsip/include/pjsua2/media.hpp @@ -96,6 +96,13 @@ struct MediaFormatAudio : public MediaFormat MediaFormatAudio() : clockRate(0), channelCount(0), frameTimeUsec(0), bitsPerSample(0), avgBps(0), maxBps(0) {} + + /** + * Initialization + */ + void init(pj_uint32_t formatId, unsigned clockRate, unsigned channelCount, + int frameTimeUsec, int bitsPerSample, + pj_uint32_t avgBps=0, pj_uint32_t maxBps=0); }; /** @@ -119,6 +126,21 @@ struct MediaFormatVideo : public MediaFormat * Export to pjmedia_format. */ pjmedia_format toPj() const; + +public: + /** + * Default constructor + */ + MediaFormatVideo() : width(0), height(0), fpsNum(0), + fpsDenum(1), avgBps(0), maxBps(0) + {} + + /** + * Initialization + */ + void init(pj_uint32_t formatId, unsigned width, unsigned height, + int fpsNum, int fpsDenum=1, + pj_uint32_t avgBps=0, pj_uint32_t maxBps=0); }; /** Array of MediaFormatAudio */ @@ -2026,12 +2048,16 @@ struct VideoPreviewOpParam { unsigned windowFlags; /** - * Media format video. If left uninitialized, this parameter will not be used and - * the capture device will be opened using PJMEDIA wrapper default format, - * e.g: + * Media format video. By default, this parameter is uninitialized + * and will not be used. + * + * To initialize it, use MediaFormatVideo::init(). + * If left uninitialized, the capture device will be opened using + * PJMEDIA wrapper default format, e.g: * - Android : PJMEDIA_FORMAT_I420 using the first supported size and 15fps * - iOS : PJMEDIA_FORMAT_BGRA using size 352x288 and 15fps - * Note that when the preview is already opened, this setting will be ignored. + * Note that when the preview is already opened, this setting will be + * ignored. */ MediaFormatVideo format; diff --git a/pjsip/src/pjsua2/media.cpp b/pjsip/src/pjsua2/media.cpp index f83d122e79..76830ca834 100644 --- a/pjsip/src/pjsua2/media.cpp +++ b/pjsip/src/pjsua2/media.cpp @@ -70,6 +70,22 @@ pjmedia_format MediaFormatAudio::toPj() const return pj_format; } +void MediaFormatAudio::init(pj_uint32_t formatId, + unsigned clockRate, unsigned channelCount, + int frameTimeUsec, int bitsPerSample, + pj_uint32_t avgBps, pj_uint32_t maxBps) +{ + type = PJMEDIA_TYPE_AUDIO; + id = formatId; + + this->clockRate = clockRate; + this->channelCount = channelCount; + this->frameTimeUsec = frameTimeUsec; + this->bitsPerSample = bitsPerSample; + this->avgBps = avgBps; + this->maxBps = maxBps; +} + /////////////////////////////////////////////////////////////////////////////// /* Audio Media operations. */ void ConfPortInfo::fromPj(const pjsua_conf_port_info &port_info) @@ -1559,6 +1575,27 @@ pjmedia_format MediaFormatVideo::toPj() const return pj_format; } +void MediaFormatVideo::init(pj_uint32_t formatId, + unsigned width, unsigned height, + int fpsNum, int fpsDenum, + pj_uint32_t avgBps, pj_uint32_t maxBps) +{ +#if PJSUA_HAS_VIDEO + type = PJMEDIA_TYPE_VIDEO; + id = formatId; + + this->width = width; + this->height = height; + this->fpsNum = fpsNum; + this->fpsDenum = fpsDenum; + this->avgBps = avgBps; + this->maxBps = maxBps; +#else + type = PJMEDIA_TYPE_UNKNOWN; +#endif +} + + /////////////////////////////////////////////////////////////////////////////// void VideoDevInfo::fromPj(const pjmedia_vid_dev_info &dev_info) { From 0d2ebdc7e33b08f623e035ba770b0107632d7d73 Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 17 Apr 2024 11:22:14 +0800 Subject: [PATCH 014/491] Fixed incorrect SDP buffer length calculation (#3924) --- pjmedia/src/pjmedia/sdp.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pjmedia/src/pjmedia/sdp.c b/pjmedia/src/pjmedia/sdp.c index c1ed796acd..a159721b29 100644 --- a/pjmedia/src/pjmedia/sdp.c +++ b/pjmedia/src/pjmedia/sdp.c @@ -895,9 +895,11 @@ static int print_session(const pjmedia_sdp_session *ses, int printed; /* Check length for v= and o= lines. */ - if (len < 5+ - 2+ses->origin.user.slen+18+ - ses->origin.net_type.slen+ses->origin.addr.slen + 2) + if (len < 5 + 2 + ses->origin.user.slen + + 20 + 20 + 3 + /* max digits of origin.id and version + + * whitespaces */ + ses->origin.net_type.slen + ses->origin.addr_type.slen + + ses->origin.addr.slen + 2 + 2) { return -1; } @@ -959,7 +961,7 @@ static int print_session(const pjmedia_sdp_session *ses, } /* Time */ - if ((end-p) < 24) { + if ((end-p) < 2+20+1+20+2) { return -1; } *p++ = 't'; From 472bda5087619db515723fbf3b2db6ad18fad830 Mon Sep 17 00:00:00 2001 From: sauwming Date: Tue, 23 Apr 2024 12:59:52 +0800 Subject: [PATCH 015/491] Support Push Notification in iOS sample app (#3913) --- .../ios/ipjsua.xcodeproj/project.pbxproj | 34 +- .../src/pjsua/ios/ipjsua/ipjsua-Info.plist | 2 + .../src/pjsua/ios/ipjsua/ipjsua.entitlements | 16 + .../src/pjsua/ios/ipjsua/ipjsuaAppDelegate.h | 11 +- .../src/pjsua/ios/ipjsua/ipjsuaAppDelegate.m | 544 ++++++++++++++---- pjsip-apps/src/pjsua/pjsua_app.c | 5 + pjsip/src/pjsua-lib/pjsua_acc.c | 3 +- 7 files changed, 506 insertions(+), 109 deletions(-) create mode 100644 pjsip-apps/src/pjsua/ios/ipjsua/ipjsua.entitlements diff --git a/pjsip-apps/src/pjsua/ios/ipjsua.xcodeproj/project.pbxproj b/pjsip-apps/src/pjsua/ios/ipjsua.xcodeproj/project.pbxproj index 4fe446aef5..9a70996b7f 100644 --- a/pjsip-apps/src/pjsua/ios/ipjsua.xcodeproj/project.pbxproj +++ b/pjsip-apps/src/pjsua/ios/ipjsua.xcodeproj/project.pbxproj @@ -42,6 +42,9 @@ 3AF0582816F050780046B835 /* ipjsuaViewController_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3AF0582616F050780046B835 /* ipjsuaViewController_iPad.xib */; }; 3AF253001EFBD15E00213893 /* libyuv.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF252FF1EFBD15E00213893 /* libyuv.a */; }; 3AF253021EFBD36E00213893 /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF253011EFBD36E00213893 /* VideoToolbox.framework */; }; + 3AF9B5422B8890880043987D /* PushKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF9B5412B8890880043987D /* PushKit.framework */; }; + 3AF9B5462B8890F40043987D /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF9B5442B8890F40043987D /* UserNotifications.framework */; }; + 3AF9B5482BA407AD0043987D /* CallKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF9B5472BA407AD0043987D /* CallKit.framework */; }; 7485A6AF1F09AAE500122F1A /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 7485A6AE1F09AAE500122F1A /* Reachability.m */; }; 7485A6B11F09B2D500122F1A /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7485A6B01F09B2D500122F1A /* SystemConfiguration.framework */; }; E5E991E61B67A45500017E67 /* libg7221codec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E991D41B67A45500017E67 /* libg7221codec.a */; }; @@ -105,6 +108,11 @@ 3AF0582716F050780046B835 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ipjsuaViewController_iPad.xib; sourceTree = ""; }; 3AF252FF1EFBD15E00213893 /* libyuv.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libyuv.a; sourceTree = ""; }; 3AF253011EFBD36E00213893 /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; }; + 3AF9B5402B88896D0043987D /* ipjsua.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ipjsua.entitlements; sourceTree = ""; }; + 3AF9B5412B8890880043987D /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = System/Library/Frameworks/PushKit.framework; sourceTree = SDKROOT; }; + 3AF9B5432B8890F40043987D /* UserNotificationsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotificationsUI.framework; path = System/Library/Frameworks/UserNotificationsUI.framework; sourceTree = SDKROOT; }; + 3AF9B5442B8890F40043987D /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; }; + 3AF9B5472BA407AD0043987D /* CallKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CallKit.framework; path = System/Library/Frameworks/CallKit.framework; sourceTree = SDKROOT; }; 7485A6AD1F09AAE500122F1A /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = ""; }; 7485A6AE1F09AAE500122F1A /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = ""; }; 7485A6B01F09B2D500122F1A /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; @@ -138,6 +146,7 @@ 3AF253021EFBD36E00213893 /* VideoToolbox.framework in Frameworks */, E5E991EC1B67A45500017E67 /* libpjmedia-codec.a in Frameworks */, 3AA31FF818F3FB4C00112C3D /* CFNetwork.framework in Frameworks */, + 3AF9B5422B8890880043987D /* PushKit.framework in Frameworks */, E5E991E61B67A45500017E67 /* libg7221codec.a in Frameworks */, E5E991EB1B67A45500017E67 /* libpjmedia-audiodev.a in Frameworks */, 3AA31FFB18F3FB4C00112C3D /* CoreImage.framework in Frameworks */, @@ -152,8 +161,10 @@ E5E991EE1B67A45500017E67 /* libpjmedia.a in Frameworks */, E5E991EA1B67A45500017E67 /* libpjlib-util.a in Frameworks */, E5E991ED1B67A45500017E67 /* libpjmedia-videodev.a in Frameworks */, + 3AF9B5462B8890F40043987D /* UserNotifications.framework in Frameworks */, E5E991E81B67A45500017E67 /* libilbccodec.a in Frameworks */, 3A4E3B5B2B6205BA0016735C /* Network.framework in Frameworks */, + 3AF9B5482BA407AD0043987D /* CallKit.framework in Frameworks */, 3AA3200118F3FB4C00112C3D /* CoreGraphics.framework in Frameworks */, 3AA31FF918F3FB4C00112C3D /* CoreAudio.framework in Frameworks */, 3AA31FFD18F3FB4C00112C3D /* CoreVideo.framework in Frameworks */, @@ -211,6 +222,10 @@ 3AF0580716F050770046B835 /* Frameworks */ = { isa = PBXGroup; children = ( + 3AF9B5472BA407AD0043987D /* CallKit.framework */, + 3AF9B5442B8890F40043987D /* UserNotifications.framework */, + 3AF9B5432B8890F40043987D /* UserNotificationsUI.framework */, + 3AF9B5412B8890880043987D /* PushKit.framework */, 3A4E3B5A2B6205B90016735C /* Network.framework */, 3A4E3B522B61FEAB0016735C /* Metal.framework */, 3A4E3B532B61FEAB0016735C /* MetalKit.framework */, @@ -237,6 +252,7 @@ 3AF0580E16F050770046B835 /* ipjsua */ = { isa = PBXGroup; children = ( + 3AF9B5402B88896D0043987D /* ipjsua.entitlements */, 3A92068D16F1D1A100D49F96 /* pjsua */, 3AF0581716F050780046B835 /* ipjsuaAppDelegate.h */, 3AF0581816F050780046B835 /* ipjsuaAppDelegate.m */, @@ -513,7 +529,9 @@ 3AF0582C16F050780046B835 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_ENTITLEMENTS = ipjsua/ipjsua.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 93NHJQ455P; @@ -545,10 +563,11 @@ "$(PROJECT_DIR)", ); ONLY_ACTIVE_ARCH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "com.teluu.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_BUNDLE_IDENTIFIER = "com.teluupush.--PRODUCT-NAME-rfc1034identifier-"; + "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.teluupush.ipjsua; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Teluu Profile"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "ipjsua Push"; VALID_ARCHS = arm64; WRAPPER_EXTENSION = app; }; @@ -557,7 +576,9 @@ 3AF0582D16F050780046B835 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_ENTITLEMENTS = ipjsua/ipjsua.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 93NHJQ455P; @@ -588,10 +609,11 @@ "$(PROJECT_DIR)", ); ONLY_ACTIVE_ARCH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "com.teluu.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_BUNDLE_IDENTIFIER = "com.teluupush.--PRODUCT-NAME-rfc1034identifier-"; + "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.teluupush.ipjsua; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Teluu Profile"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "ipjsua Push"; VALID_ARCHS = arm64; WRAPPER_EXTENSION = app; }; diff --git a/pjsip-apps/src/pjsua/ios/ipjsua/ipjsua-Info.plist b/pjsip-apps/src/pjsua/ios/ipjsua/ipjsua-Info.plist index e4aa56d774..1b8f68bb45 100644 --- a/pjsip-apps/src/pjsua/ios/ipjsua/ipjsua-Info.plist +++ b/pjsip-apps/src/pjsua/ios/ipjsua/ipjsua-Info.plist @@ -4,6 +4,8 @@ CFBundleDevelopmentRegion en + AppIdentifierPrefix + $(AppIdentifierPrefix) CFBundleDisplayName ${PRODUCT_NAME} CFBundleExecutable diff --git a/pjsip-apps/src/pjsua/ios/ipjsua/ipjsua.entitlements b/pjsip-apps/src/pjsua/ios/ipjsua/ipjsua.entitlements new file mode 100644 index 0000000000..e3ddf78064 --- /dev/null +++ b/pjsip-apps/src/pjsua/ios/ipjsua/ipjsua.entitlements @@ -0,0 +1,16 @@ + + + + + aps-environment + development + com.apple.security.app-sandbox + + com.apple.security.device.audio-input + + com.apple.security.device.camera + + com.apple.security.network.client + + + diff --git a/pjsip-apps/src/pjsua/ios/ipjsua/ipjsuaAppDelegate.h b/pjsip-apps/src/pjsua/ios/ipjsua/ipjsuaAppDelegate.h index 7f01d2759a..fbf6c5e873 100644 --- a/pjsip-apps/src/pjsua/ios/ipjsua/ipjsuaAppDelegate.h +++ b/pjsip-apps/src/pjsua/ios/ipjsua/ipjsuaAppDelegate.h @@ -18,14 +18,23 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#import +#import #import +#import @class ipjsuaViewController; -@interface ipjsuaAppDelegate : UIResponder +@interface ipjsuaAppDelegate : UIResponder + @property (strong, nonatomic) UIWindow *window; +@property (strong, nonatomic) PKPushRegistry *voipRegistry; +@property (strong, nonatomic) NSMutableString *token; +@property (strong, nonatomic) CXProvider *provider; + @property (strong, nonatomic) ipjsuaViewController *viewController; @end diff --git a/pjsip-apps/src/pjsua/ios/ipjsua/ipjsuaAppDelegate.m b/pjsip-apps/src/pjsua/ios/ipjsua/ipjsuaAppDelegate.m index 1d56c82462..8a6ef9dc32 100644 --- a/pjsip-apps/src/pjsua/ios/ipjsua/ipjsuaAppDelegate.m +++ b/pjsip-apps/src/pjsua/ios/ipjsua/ipjsuaAppDelegate.m @@ -22,6 +22,8 @@ #import #import #import +#import +#import #include "../../pjsua_app.h" #include "../../pjsua_app_config.h" @@ -33,14 +35,172 @@ @implementation ipjsuaAppDelegate #define THIS_FILE "ipjsuaAppDelegate.m" -#define KEEP_ALIVE_INTERVAL 600 +/* Specify if we use push notification. */ +#define USE_PUSH_NOTIFICATION 1 + +/* Specify the timeout (in sec) to wait for the incoming INVITE + * to come after we receive push notification. + */ +#define MAX_INV_TIMEOUT 10 + +/* Account details. */ +#define SIP_DOMAIN "sip.pjsip.org" +#define SIP_USER "test" +#define SIP_PASSWD "test" ipjsuaAppDelegate *app; static pjsua_app_cfg_t app_cfg; static bool isShuttingDown; static char **restartArgv; static int restartArgc; -Reachability *internetReach; +Reachability *internetReach; + +/* Mapping between CallKit UUID and pjsua_call_id. */ +NSMutableDictionary *call_map; + +enum { + REREGISTER = 1, + ANSWER_CALL, + END_CALL, + ACTIVATE_AUDIO, + DEACTIVATE_AUDIO, + HANDLE_IP_CHANGE, + HANDLE_ORI_CHANGE +}; + +#define REGISTER_THREAD \ + static pj_thread_desc a_thread_desc; \ + static pj_thread_t *a_thread; \ + if (!pj_thread_is_registered()) { \ + pj_thread_register("ipjsua", a_thread_desc, &a_thread); \ + } + +#define SCHEDULE_TIMER(action) \ +{ \ + REGISTER_THREAD \ + pjsua_schedule_timer2(pjsip_funcs, (void *)action, 0); \ +} + +static void pjsip_funcs(void *user_data) +{ + /* IMPORTANT: + * We need to call PJSIP API from a separate thread since + * PJSIP API can potentially block the main/GUI thread. + * And make sure we don't use Apple's Dispatch / gcd since + * it's incompatible with POSIX threads. + * In this example, we take advantage of PJSUA's timer thread + * to invoke PJSIP APIs. For a more complex application, + * it is recommended to create your own separate thread + * instead for this purpose. + */ + long code = (long)user_data & 0xF; + if (code == REREGISTER) { + for (unsigned i = 0; i < pjsua_acc_get_count(); ++i) { + if (pjsua_acc_is_valid(i)) { + pjsua_acc_set_registration(i, PJ_TRUE); + } + } + } else if (code == ANSWER_CALL) { + pj_status_t status; + pjsua_call_id call_id = (pjsua_call_id)((long)user_data & 0xFF0) >> 4; + + status = pjsua_call_answer(call_id, PJSIP_SC_OK, NULL, NULL); + if (status != PJ_SUCCESS) { + NSUUID *uuid =(__bridge NSUUID *)pjsua_call_get_user_data(call_id); + if (uuid) { + [app.provider reportCallWithUUID:uuid + endedAtDate:[NSDate date] + reason:CXCallEndedReasonFailed]; + } + } + } else if (code == END_CALL) { + pj_status_t status; + pjsua_call_id call_id = (pjsua_call_id)((long)user_data & 0xFF0) >> 4; + + status = pjsua_call_hangup(call_id, PJSIP_SC_OK, NULL, NULL); + if (status != PJ_SUCCESS) { + NSUUID *uuid =(__bridge NSUUID *)pjsua_call_get_user_data(call_id); + if (uuid) { + [app.provider reportCallWithUUID:uuid + endedAtDate:[NSDate date] + reason:CXCallEndedReasonFailed]; + } + } + } else if (code == ACTIVATE_AUDIO) { + pjsua_call_info call_info; + pjsua_call_id ids[PJSUA_MAX_CALLS]; + unsigned count = PJSUA_MAX_CALLS; + + /* If we use CallKit, sound device may not work until audio session + * becomes active, so we need to force reopen sound device here. + */ + pjsua_set_no_snd_dev(); + pjsua_set_snd_dev(PJSUA_SND_DEFAULT_CAPTURE_DEV, + PJSUA_SND_DEFAULT_PLAYBACK_DEV); + + pjsua_enum_calls(ids, &count); + for (unsigned i = 0; i < count; i++) { + pjsua_call_get_info(i, &call_info); + + for (unsigned mi = 0; mi < call_info.media_cnt; ++mi) { + if (call_info.media[mi].type == PJMEDIA_TYPE_AUDIO && + (call_info.media[mi].status == PJSUA_CALL_MEDIA_ACTIVE || + call_info.media[mi].status == PJSUA_CALL_MEDIA_REMOTE_HOLD)) + { + pjsua_conf_port_id call_conf_slot; + call_conf_slot = call_info.media[mi].stream.aud.conf_slot; + pjsua_conf_connect(0, call_conf_slot); + pjsua_conf_connect(call_conf_slot, 0); + } + } + } + } else if (code == DEACTIVATE_AUDIO) { + NSLog(@"Deactivating audio session, is sound active: %d", + pjsua_snd_is_active()); + if (!pjsua_snd_is_active()) { + [[AVAudioSession sharedInstance] setActive:NO + withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation + error:nil]; + } + } else if (code == HANDLE_IP_CHANGE) { + pjsua_ip_change_param param; + pjsua_ip_change_param_default(¶m); + pjsua_handle_ip_change(¶m); + } else if (code == HANDLE_ORI_CHANGE) { +#if PJSUA_HAS_VIDEO + const pjmedia_orient pj_ori[4] = + { + PJMEDIA_ORIENT_ROTATE_90DEG, /* UIDeviceOrientationPortrait */ + PJMEDIA_ORIENT_ROTATE_270DEG, /* UIDeviceOrientationPortraitUpsideDown */ + PJMEDIA_ORIENT_ROTATE_180DEG, /* UIDeviceOrientationLandscapeLeft, + home button on the right side */ + PJMEDIA_ORIENT_NATURAL /* UIDeviceOrientationLandscapeRight, + home button on the left side */ + }; + static UIDeviceOrientation prev_ori = 0; + UIDeviceOrientation dev_ori = [[UIDevice currentDevice] orientation]; + int i; + + if (dev_ori == prev_ori) return; + + NSLog(@"Device orientation changed: %d", (int)(prev_ori = dev_ori)); + + if (dev_ori >= UIDeviceOrientationPortrait && + dev_ori <= UIDeviceOrientationLandscapeRight) + { + /* Here we set the orientation for all video devices. + * This may return failure for renderer devices or for + * capture devices which do not support orientation setting, + * we can simply ignore them. + */ + for (i = pjsua_vid_dev_count()-1; i >= 0; i--) { + pjsua_vid_dev_set_setting(i, PJMEDIA_VID_DEV_CAP_ORIENTATION, + &pj_ori[dev_ori-1], PJ_TRUE); + } + } +#endif + } +} - (void) updateWithReachability: (Reachability *)curReach { @@ -48,18 +208,18 @@ - (void) updateWithReachability: (Reachability *)curReach BOOL connectionRequired = [curReach connectionRequired]; switch (netStatus) { case NotReachable: - PJ_LOG(3,("", "Access Not Available..")); + NSLog(@"Access Not Available.."); connectionRequired= NO; break; case ReachableViaWiFi: - PJ_LOG(3,("", "Reachable WiFi..")); + NSLog(@"Reachable WiFi.."); break; case ReachableViaWWAN: - PJ_LOG(3,("", "Reachable WWAN..")); + NSLog(@"Reachable WWAN.."); break; } if (connectionRequired) { - PJ_LOG(3,("", "Connection Required")); + NSLog(@"Connection Required"); } } @@ -68,18 +228,34 @@ - (void)reachabilityChanged: (NSNotification *)note { Reachability* curReach = [note object]; NSParameterAssert([curReach isKindOfClass: [Reachability class]]); - PJ_LOG(3,("", "reachability changed..")); + NSLog(@"reachability changed.."); [self updateWithReachability: curReach]; if ([curReach currentReachabilityStatus] != NotReachable && ![curReach connectionRequired]) { - pjsua_ip_change_param param; - pjsua_ip_change_param_default(¶m); - pjsua_handle_ip_change(¶m); + SCHEDULE_TIMER(HANDLE_IP_CHANGE); } } +- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type +{ + if ([credentials.token length] == 0) { + NSLog(@"voip token NULL"); + return; + } + + /* Get device token */ + const char *data = [credentials.token bytes]; + self.token = [NSMutableString string]; + for (NSUInteger i = 0; i < [credentials.token length]; i++) { + [self.token appendFormat:@"%02.2hhx", data[i]]; + } + NSLog(@"VOIP Push Notification Token: %@", self.token); + + /* Now start pjsua */ + [NSThread detachNewThreadSelector:@selector(pjsuaStart) toTarget:self withObject:nil]; +} void displayLog(const char *msg, int len) { @@ -171,6 +347,57 @@ - (void)pjsuaStart object:[UIDevice currentDevice]]; }); + static char contact_uri_buf[1024]; + pjsua_acc_config cfg; + + pjsua_acc_config_default(&cfg); + cfg.id = pj_str("sip:" SIP_USER "@" SIP_DOMAIN); + cfg.reg_uri = pj_str("sip:" SIP_DOMAIN ";transport=tcp"); + cfg.cred_count = 1; + cfg.cred_info[0].realm = pj_str(SIP_DOMAIN); + cfg.cred_info[0].scheme = pj_str("digest"); + cfg.cred_info[0].username = pj_str(SIP_USER); + cfg.cred_info[0].data = pj_str(SIP_PASSWD); + /* Uncomment this to enable video. Note that video can only + * work in the foreground. + */ + // app_config_init_video(&cfg); + + /* If we have Push Notification token, put it in the registration + * Contact URI params. + */ + if ([self.token length]) { + /* According to RFC 8599: + * - pn-provider is the Push Notification Service provider. Here + * we use APNS (Apple Push Notification Service). + * - pn-param is composed of Team ID and Topic separated by + * a period (.). + * The Topic consists of the Bundle ID, the app's unique ID, + * and the app's service value ("voip" for VoIP apps), separated + * by a period (.). + * - pn-prid is the PN token. + */ + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; + NSString *bundleID = infoDictionary[@"CFBundleIdentifier"]; + NSString *teamID = infoDictionary[@"AppIdentifierPrefix"]; + NSLog(@"Team ID from settings: %@", teamID); + + pj_ansi_snprintf(contact_uri_buf, sizeof(contact_uri_buf), + ";pn-provider=apns" + ";pn-param=%s%s.voip" + ";pn-prid=%s", + (teamID? [teamID UTF8String]: "93NHJQ455P."), + [bundleID UTF8String], + [self.token UTF8String]); + cfg.reg_contact_uri_params = pj_str(contact_uri_buf); + } + status = pjsua_acc_add(&cfg, PJ_TRUE, NULL); + if (status != PJ_SUCCESS) { + char errmsg[PJ_ERR_MSG_SIZE]; + pj_strerror(status, errmsg, sizeof(errmsg)); + displayMsg(errmsg); + } + status = pjsua_app_run(PJ_TRUE); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; @@ -187,6 +414,116 @@ - (void)pjsuaStart restartArgc = 0; } +- (void) provider:(CXProvider *) provider +didActivateAudioSession:(AVAudioSession *) audioSession +{ + NSLog(@"Did activate Audio Session"); + + pjsua_schedule_timer2(pjsip_funcs, (void *)ACTIVATE_AUDIO, 0); +} + +- (void) provider:(CXProvider *) provider +didDectivateAudioSession:(AVAudioSession *) audioSession +{ + NSLog(@"Did deactivate Audio Session"); +} + +- (void)provider:(CXProvider *)provider + performAnswerCallAction:(CXAnswerCallAction *)action +{ + NSLog(@"Perform answer call %@", action.callUUID.UUIDString); + + /* User has answered the call, but we may need to wait for + * the incoming INVITE to come. + */ + for (int i = MAX_INV_TIMEOUT * 1000 / 100; i >= 0; i--) { + if (call_map[action.callUUID].intValue != PJSUA_INVALID_ID) break; + [NSThread sleepForTimeInterval:0.1]; + } + + [action fulfill]; + + pjsua_call_id call_id = call_map[action.callUUID].intValue; + if (call_id == PJSUA_INVALID_ID) { + [app.provider reportCallWithUUID:action.callUUID + endedAtDate:[NSDate date] + reason:CXCallEndedReasonFailed]; + return; + } + + long code = (long)ANSWER_CALL | (call_id << 4); + pjsua_schedule_timer2(pjsip_funcs, (void *)code, 0); +} + +- (void)provider:(CXProvider *)provider + performEndCallAction:(CXEndCallAction *)action +{ + NSLog(@"Perform end call %@", action.callUUID.UUIDString); + + /* User has answered the call, but we may need to wait for + * the incoming INVITE to come. + */ + for (int i = MAX_INV_TIMEOUT * 1000 / 100; i >= 0; i--) { + if (call_map[action.callUUID].intValue != PJSUA_INVALID_ID) break; + [NSThread sleepForTimeInterval:0.1]; + } + + [action fulfill]; + + pjsua_call_id call_id = call_map[action.callUUID].intValue; + if (call_id == PJSUA_INVALID_ID) { + [app.provider reportCallWithUUID:action.callUUID + endedAtDate:[NSDate date] + reason:CXCallEndedReasonFailed]; + return; + } + + long code = (long)END_CALL | (call_id << 4); + pjsua_schedule_timer2(pjsip_funcs, (void *)code, 0); +} + +- (void)pushRegistry:(PKPushRegistry *)registry + didReceiveIncomingPushWithPayload:(PKPushPayload *)payload + forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion +{ + /* Handle incoming VoIP push notification. */ + NSUUID *uuid = [NSUUID UUID]; + call_map[uuid] = @(PJSUA_INVALID_ID); + NSLog(@"Receiving push notification %@", uuid.UUIDString); + + /* Re-register, so the server will send us the suspended INVITE. */ + SCHEDULE_TIMER(REREGISTER); + + /* Activate audio session. */ + AVAudioSession *audioSession = [AVAudioSession sharedInstance]; + NSError *error = nil; + if (![audioSession setCategory:AVAudioSessionCategoryPlayAndRecord + mode:AVAudioSessionModeVoiceChat + options:0 error:&error]) + { + NSLog(@"Error setting up audio session: %@", error.localizedDescription); + } + if (![audioSession setActive:YES error:&error]) { + NSLog(@"Error activating audio session: %@", error.localizedDescription); + } + + /* Report the incoming call to the system using CallKit. */ + CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init]; + [self.provider reportNewIncomingCallWithUUID:uuid + update:callUpdate + completion:^(NSError * _Nullable error) + { + if (error) { + NSLog(@"Error reporting incoming call: %@", + error.localizedDescription); + [call_map removeObjectForKey:uuid]; + } + }]; + + /* Call the completion handler when you have finished processing the incoming call. */ + completion(); +} + - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; @@ -198,7 +535,62 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( } self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible]; - + +#if USE_PUSH_NOTIFICATION + call_map = [NSMutableDictionary dictionary]; + + /* Set up a push notification registry for Voice over IP (VoIP). */ + self.voipRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()]; + self.voipRegistry.delegate = self; + self.voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP]; + + /* Request permission for push notifications. */ + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; + center.delegate = self; + [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | + UNAuthorizationOptionBadge | + UNAuthorizationOptionSound) + completionHandler:^(BOOL granted, NSError * _Nullable error) + { + NSLog(@"Notification request %sgranted", (granted ? "" : "not")); + + if (granted) { + dispatch_async(dispatch_get_main_queue(), ^{ + [application registerForRemoteNotifications]; + }); + } + }]; + + /* Note that opening audio device when in the background will not trigger + * permission request, so we won't be able to use audio device. + * Thus, we need to request permission now. + */ + [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) + { + NSLog(@"Microphone access %sgranted", (granted ? "" : "not")); + }]; + + /* We need to request local network access permission as well to + * immediately send media traffic when in the background. + * Due to its complexity, the code is not included here in the sample app. + * Please refer to the Apple "Local Network Privacy" FAQ for more details. + */ + + /* Create CallKit provider. */ + CXProviderConfiguration *configuration = [[CXProviderConfiguration alloc] + initWithLocalizedName:@"ipjsua"]; + configuration.maximumCallGroups = 1; + configuration.maximumCallsPerCallGroup = 1; + self.provider = [[CXProvider alloc] initWithConfiguration:configuration]; + [self.provider setDelegate:self queue:nil]; +#else + /* Start pjsua app thread immediately, otherwise we do it after push + * notification setup completes. + */ + [NSThread detachNewThreadSelector:@selector(pjsuaStart) toTarget:self + withObject:nil]; +#endif + /* Observe the kNetworkReachabilityChangedNotification. When that * notification is posted, the method "reachabilityChanged" will be called. */ @@ -211,9 +603,6 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [self updateWithReachability: internetReach]; app = self; - - /* Start pjsua app thread */ - [NSThread detachNewThreadSelector:@selector(pjsuaStart) toTarget:self withObject:nil]; return YES; } @@ -226,78 +615,14 @@ - (void)applicationWillResignActive:(UIApplication *)application - (void)orientationChanged:(NSNotification *)note { -#if PJSUA_HAS_VIDEO - const pjmedia_orient pj_ori[4] = - { - PJMEDIA_ORIENT_ROTATE_90DEG, /* UIDeviceOrientationPortrait */ - PJMEDIA_ORIENT_ROTATE_270DEG, /* UIDeviceOrientationPortraitUpsideDown */ - PJMEDIA_ORIENT_ROTATE_180DEG, /* UIDeviceOrientationLandscapeLeft, - home button on the right side */ - PJMEDIA_ORIENT_NATURAL /* UIDeviceOrientationLandscapeRight, - home button on the left side */ - }; - static pj_thread_desc a_thread_desc; - static pj_thread_t *a_thread; - static UIDeviceOrientation prev_ori = 0; - UIDeviceOrientation dev_ori = [[UIDevice currentDevice] orientation]; - int i; - - if (dev_ori == prev_ori) return; - - NSLog(@"Device orientation changed: %d", (int)(prev_ori = dev_ori)); - - if (dev_ori >= UIDeviceOrientationPortrait && - dev_ori <= UIDeviceOrientationLandscapeRight) - { - if (!pj_thread_is_registered()) { - pj_thread_register("ipjsua", a_thread_desc, &a_thread); - } - - /* Here we set the orientation for all video devices. - * This may return failure for renderer devices or for - * capture devices which do not support orientation setting, - * we can simply ignore them. - */ - for (i = pjsua_vid_dev_count()-1; i >= 0; i--) { - pjsua_vid_dev_set_setting(i, PJMEDIA_VID_DEV_CAP_ORIENTATION, - &pj_ori[dev_ori-1], PJ_TRUE); - } - } -#endif -} - -- (void)keepAlive { - static pj_thread_desc a_thread_desc; - static pj_thread_t *a_thread; - int i; - - if (!pj_thread_is_registered()) { - pj_thread_register("ipjsua", a_thread_desc, &a_thread); - } - - /* Since iOS requires that the minimum keep alive interval is 600s, - * application needs to make sure that the account's registration - * timeout is long enough. - */ - for (i = 0; i < (int)pjsua_acc_get_count(); ++i) { - if (pjsua_acc_is_valid(i)) { - pjsua_acc_set_registration(i, PJ_TRUE); - } - } + SCHEDULE_TIMER(HANDLE_ORI_CHANGE); } - (void)applicationDidEnterBackground:(UIApplication *)application { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. - [self performSelectorOnMainThread:@selector(keepAlive) withObject:nil waitUntilDone:YES]; - -#if 0 - /* setKeepAliveTimeout is deprecated. Use PushKit instead. */ - [application setKeepAliveTimeout:KEEP_ALIVE_INTERVAL handler: ^{ - [self performSelectorOnMainThread:@selector(keepAlive) withObject:nil waitUntilDone:YES]; - }]; -#endif + SCHEDULE_TIMER(REREGISTER); + /* Allow the re-registration to complete. */ + [NSThread sleepForTimeInterval:0.3]; } - (void)applicationWillEnterForeground:(UIApplication *)application @@ -316,27 +641,44 @@ - (void)applicationWillTerminate:(UIApplication *)application } -pj_bool_t showNotification(pjsua_call_id call_id) +pj_bool_t reportCallState(pjsua_call_id call_id) { - /* This is deprecated. Use VoIP Push Notifications with PushKit - * framework instead. - */ -#if 0 - // Create a new notification - UILocalNotification* alert = [[UILocalNotification alloc] init]; - if (alert) - { - alert.repeatInterval = 0; - alert.alertBody = @"Incoming call received..."; - /* This action just brings the app to the FG, it doesn't - * automatically answer the call (unless you specify the - * --auto-answer option). + pjsua_call_info info; + + pjsua_call_get_info(call_id, &info); + + if (info.state == PJSIP_INV_STATE_DISCONNECTED) { + NSUUID *uuid = (__bridge NSUUID *)pjsua_call_get_user_data(call_id); + if (uuid && call_map[uuid].intValue == call_id) { + [app.provider reportCallWithUUID:uuid + endedAtDate:[NSDate date] + reason:CXCallEndedReasonRemoteEnded]; + [call_map removeObjectForKey:uuid]; + } + + /* Check if we need to deactivate audio session. Note that sound device + * will only be closed after pjsua_media_config.snd_auto_close_time. */ - alert.alertAction = @"Activate app"; - - dispatch_async(dispatch_get_main_queue(), - ^{[[UIApplication sharedApplication] - presentLocalNotificationNow:alert];}); + pjsua_schedule_timer2(pjsip_funcs, (void *)DEACTIVATE_AUDIO, 1500); + } + + return PJ_FALSE; +} + +pj_bool_t showNotification(pjsua_call_id call_id) +{ +#if USE_PUSH_NOTIFICATION + NSLog(@"Receiving incoming call %d", call_id); + + for (NSUUID *uuid in call_map) { + if (call_map[uuid].intValue == PJSUA_INVALID_ID) { + NSLog(@"Associating incoming call %d with UUID %@", + call_id, uuid.UUIDString); + call_map[uuid] = @(call_id); + pjsua_call_set_user_data(call_id, (__bridge void *)uuid); + + break; + } } #endif diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c index 3c819b34ec..37260251e7 100644 --- a/pjsip-apps/src/pjsua/pjsua_app.c +++ b/pjsip-apps/src/pjsua/pjsua_app.c @@ -47,6 +47,7 @@ static void stereo_demo(); #ifdef USE_GUI pj_bool_t showNotification(pjsua_call_id call_id); +pj_bool_t reportCallState(pjsua_call_id call_id); #endif static void ringback_start(pjsua_call_id call_id); @@ -171,6 +172,10 @@ static void on_call_state(pjsua_call_id call_id, pjsip_event *e) PJ_UNUSED_ARG(e); +#ifdef USE_GUI + reportCallState(call_id); +#endif + pjsua_call_get_info(call_id, &call_info); if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) { diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index c113467b42..0326f5480c 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -1605,7 +1605,8 @@ static void update_regc_contact(pjsua_acc *acc) acc->cfg.reg_contact_params.slen + acc->cfg.reg_contact_uri_params.slen + (need_outbound? - (acc->rfc5626_instprm.slen + acc->rfc5626_regprm.slen): 0); + (acc->rfc5626_instprm.slen + acc->rfc5626_regprm.slen): 0) + + 5; /* allowance */ if (len > acc->contact.slen) { reg_contact.ptr = (char*) pj_pool_alloc(acc->pool, len); From 4f3df90a3eb7e732283a4e312c7a3a003d791bd3 Mon Sep 17 00:00:00 2001 From: sauwming Date: Tue, 23 Apr 2024 17:01:38 +0800 Subject: [PATCH 016/491] Fixed PJSUA2 API to get/set Opus config (#3935) --- pjsip/include/pjsua2/endpoint.hpp | 2 -- pjsip/src/pjsua2/endpoint.cpp | 39 ++++++++++++++++++------------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/pjsip/include/pjsua2/endpoint.hpp b/pjsip/include/pjsua2/endpoint.hpp index 0d2bfdc882..a40c38df57 100644 --- a/pjsip/include/pjsua2/endpoint.hpp +++ b/pjsip/include/pjsua2/endpoint.hpp @@ -1779,7 +1779,6 @@ class Endpoint */ void resetVideoCodecParam(const string &codec_id) PJSUA2_THROW(Error); -#if defined(PJMEDIA_HAS_OPUS_CODEC) && (PJMEDIA_HAS_OPUS_CODEC!=0) /** * Get codec Opus config. * @@ -1794,7 +1793,6 @@ class Endpoint */ void setCodecOpusConfig(const CodecOpusConfig &opus_cfg) PJSUA2_THROW(Error); -#endif /** * Enumerate all SRTP crypto-suite names. diff --git a/pjsip/src/pjsua2/endpoint.cpp b/pjsip/src/pjsua2/endpoint.cpp index f206f27e3f..417a123cfd 100644 --- a/pjsip/src/pjsua2/endpoint.cpp +++ b/pjsip/src/pjsua2/endpoint.cpp @@ -2490,34 +2490,41 @@ void Endpoint::codecSetParam(const string &codec_id, PJSUA2_CHECK_EXPR( pjsua_codec_set_param(&codec_str, &pj_param) ); } -#if defined(PJMEDIA_HAS_OPUS_CODEC) && (PJMEDIA_HAS_OPUS_CODEC!=0) CodecOpusConfig Endpoint::getCodecOpusConfig() const PJSUA2_THROW(Error) { - pjmedia_codec_opus_config opus_cfg; - CodecOpusConfig config; + CodecOpusConfig config; +#if defined(PJMEDIA_HAS_OPUS_CODEC) && (PJMEDIA_HAS_OPUS_CODEC!=0) + pjmedia_codec_opus_config opus_cfg; - PJSUA2_CHECK_EXPR(pjmedia_codec_opus_get_config(&opus_cfg)); - config.fromPj(opus_cfg); + PJSUA2_CHECK_EXPR(pjmedia_codec_opus_get_config(&opus_cfg)); + config.fromPj(opus_cfg); +#else + PJSUA2_RAISE_ERROR(PJ_ENOTSUP); +#endif - return config; + return config; } void Endpoint::setCodecOpusConfig(const CodecOpusConfig &opus_cfg) PJSUA2_THROW(Error) { - const pj_str_t codec_id = {(char *)"opus", 4}; - pjmedia_codec_param param; - pjmedia_codec_opus_config new_opus_cfg; - - PJSUA2_CHECK_EXPR(pjsua_codec_get_param(&codec_id, ¶m)); - new_opus_cfg = opus_cfg.toPj(); - - PJSUA2_CHECK_EXPR(pjmedia_codec_opus_set_default_param(&new_opus_cfg, - ¶m)); -} +#if defined(PJMEDIA_HAS_OPUS_CODEC) && (PJMEDIA_HAS_OPUS_CODEC!=0) + const pj_str_t codec_id = {(char *)"opus", 4}; + pjmedia_codec_param param; + pjmedia_codec_opus_config new_opus_cfg; + + PJSUA2_CHECK_EXPR(pjsua_codec_get_param(&codec_id, ¶m)); + new_opus_cfg = opus_cfg.toPj(); + + PJSUA2_CHECK_EXPR(pjmedia_codec_opus_set_default_param(&new_opus_cfg, + ¶m)); +#else + PJ_UNUSED_ARG(opus_cfg); + PJSUA2_RAISE_ERROR(PJ_ENOTSUP); #endif +} void Endpoint::clearCodecInfoList(CodecInfoVector &codec_list) { From f38d781a82a2b51b51a9996d4d76bdd8e69304d4 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Mon, 29 Apr 2024 15:01:38 +0700 Subject: [PATCH 017/491] Fix bad address length check in pj_ioqueue_sendto(). (#3941) --- pjlib/src/pj/ioqueue_common_abs.c | 2 +- pjlib/src/pj/ioqueue_common_abs.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pjlib/src/pj/ioqueue_common_abs.c b/pjlib/src/pj/ioqueue_common_abs.c index f5ad390388..a9a6a9cfd0 100644 --- a/pjlib/src/pj/ioqueue_common_abs.c +++ b/pjlib/src/pj/ioqueue_common_abs.c @@ -1056,7 +1056,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key, /* * Check that address storage can hold the address parameter. */ - PJ_ASSERT_RETURN(addrlen <= (int)sizeof(pj_sockaddr_in), PJ_EBUG); + PJ_ASSERT_RETURN(addrlen <= (int)sizeof(pj_sockaddr), PJ_EBUG); /* * Schedule asynchronous send. diff --git a/pjlib/src/pj/ioqueue_common_abs.h b/pjlib/src/pj/ioqueue_common_abs.h index 77787b15c6..bcc11df45e 100644 --- a/pjlib/src/pj/ioqueue_common_abs.h +++ b/pjlib/src/pj/ioqueue_common_abs.h @@ -63,7 +63,7 @@ struct write_operation pj_size_t size; pj_ssize_t written; unsigned flags; - pj_sockaddr_in rmt_addr; + pj_sockaddr rmt_addr; int rmt_addrlen; }; From f406002f688eb58c3461cb90f6ac9c5b3459fbfd Mon Sep 17 00:00:00 2001 From: sauwming Date: Tue, 7 May 2024 08:02:17 +0800 Subject: [PATCH 018/491] Fix warning of uninitialized value in fuzz-crypto (#3946) --- tests/fuzz/fuzz-crypto.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/fuzz/fuzz-crypto.c b/tests/fuzz/fuzz-crypto.c index 38a5ab1edc..dcf5cbdd84 100644 --- a/tests/fuzz/fuzz-crypto.c +++ b/tests/fuzz/fuzz-crypto.c @@ -45,7 +45,7 @@ void encode_base64_differential(const uint8_t *Data, size_t Size) { //OPENSSL BIO *bio, *bio_mem; - char *ssl_output; + char *ssl_output = NULL; int ssl_output_len; bio = BIO_new(BIO_f_base64()); @@ -58,6 +58,9 @@ void encode_base64_differential(const uint8_t *Data, size_t Size) { BIO_flush(bio); ssl_output_len = BIO_get_mem_data(bio_mem, &ssl_output); + if (ssl_output_len <= 0) { + abort(); + } //Differential int result = memcmp(pj_output, ssl_output, ssl_output_len); From ca2dacf0247a0baed4173051d5d1606100c9f7f6 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 7 May 2024 09:22:35 +0700 Subject: [PATCH 019/491] Print log on successful send (#3942) --- pjsip/src/pjsip/sip_transport.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index 3b30046fec..27c8176093 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -877,6 +877,12 @@ static void transport_send_callback(pjsip_transport *transport, PJ_UNUSED_ARG(transport); + /* Print log on successful sending */ + if (size > 0) { + PJ_LOG(5,(transport->obj_name, + "%s sent successfully", pjsip_tx_data_get_info(tdata))); + } + /* Mark pending off so that app can resend/reuse txdata from inside * the callback. */ @@ -965,6 +971,13 @@ PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr, if (status != PJ_EPENDING) { tdata->is_pending = 0; + + /* Print log on successful sending */ + if (status == PJ_SUCCESS) { + PJ_LOG(5,(tr->obj_name, + "%s sent successfully", pjsip_tx_data_get_info(tdata))); + } + pjsip_tx_data_dec_ref(tdata); } From 24ac5875ffdff3f287e495e3ce81ae1d11777f3e Mon Sep 17 00:00:00 2001 From: sauwming Date: Tue, 7 May 2024 10:29:43 +0800 Subject: [PATCH 020/491] Fixed CI Mac build failure (#3947) --- .github/workflows/ci-mac.yml | 40 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index 78316c303a..d664932558 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -23,11 +23,11 @@ jobs: steps: - uses: actions/checkout@v2 - name: install dependencies - run: brew install openssl@1.1 opencore-amr swig sipp + run: brew install openssl opencore-amr swig sipp - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h - name: configure - run: CFLAGS="-g -I/usr/local/include -I/usr/local/opt/openssl@1.1/include -fPIC" LDFLAGS="-L/usr/local/lib -L/usr/local/opt/openssl@1.1/lib" CXXFLAGS="-g -fPIC" ./configure + run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" CXXFLAGS="-g -fPIC" ./configure - name: make run: make - name: set up Python @@ -47,11 +47,11 @@ jobs: steps: - uses: actions/checkout@v2 - name: install dependencies - run: brew install openssl@1.1 opencore-amr + run: brew install openssl opencore-amr - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h - name: configure - run: CFLAGS="-g -I/usr/local/include -I/usr/local/opt/openssl@1.1/include" LDFLAGS="-L/usr/local/lib -L/usr/local/opt/openssl@1.1/lib" ./configure + run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb)" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" ./configure - name: make run: make - name: disable firewall @@ -65,11 +65,11 @@ jobs: steps: - uses: actions/checkout@v2 - name: install dependencies - run: brew install openssl@1.1 opencore-amr + run: brew install openssl opencore-amr - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h - name: configure - run: CFLAGS="-g -I/usr/local/include -I/usr/local/opt/openssl@1.1/include" LDFLAGS="-L/usr/local/lib -L/usr/local/opt/openssl@1.1/lib" ./configure + run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb)" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" ./configure - name: make run: make - name: disable firewall @@ -86,9 +86,9 @@ jobs: steps: - uses: actions/checkout@v2 - name: install dependencies - run: brew install openssl@1.1 swig + run: brew install openssl swig - name: configure - run: CFLAGS="-I/usr/local/include -I/usr/local/opt/openssl@1.1/include -fPIC" LDFLAGS="-L/usr/local/lib -L/usr/local/opt/openssl@1.1/lib" CXXFLAGS="-fPIC" ./configure + run: CFLAGS="$(pkg-config --cflags openssl) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib" CXXFLAGS="-fPIC" ./configure - name: make run: make - name: set up Python @@ -123,11 +123,11 @@ jobs: steps: - uses: actions/checkout@v2 - name: install dependencies - run: brew install openssl@1.1 openh264 libvpx opencore-amr swig sipp + run: brew install openssl openh264 libvpx opencore-amr swig sipp - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h && echo "#define PJMEDIA_HAS_VIDEO 1" >> config_site.h - name: configure - run: CFLAGS="-g -I/usr/local/include -I/usr/local/opt/openssl@1.1/include -DHAS_VID_CODEC_TEST=0 -fPIC" LDFLAGS="-L/usr/local/lib -L/usr/local/opt/openssl@1.1/lib" CXXFLAGS="-g -fPIC" ./configure + run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb) -DHAS_VID_CODEC_TEST=0 -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" CXXFLAGS="-g -fPIC" ./configure - name: make run: make - name: set up Python @@ -147,11 +147,11 @@ jobs: steps: - uses: actions/checkout@v2 - name: install dependencies - run: brew install openssl@1.1 openh264 libvpx opencore-amr + run: brew install openssl openh264 libvpx opencore-amr - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h && echo "#define PJMEDIA_HAS_VIDEO 1" >> config_site.h - name: configure - run: CFLAGS="-g -I/usr/local/include -I/usr/local/opt/openssl@1.1/include" LDFLAGS="-L/usr/local/lib -L/usr/local/opt/openssl@1.1/lib" ./configure + run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb)" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" ./configure - name: make run: make - name: disable firewall @@ -165,11 +165,11 @@ jobs: steps: - uses: actions/checkout@v2 - name: install dependencies - run: brew install openssl@1.1 openh264 libvpx opencore-amr + run: brew install openssl openh264 libvpx opencore-amr - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h && echo "#define PJMEDIA_HAS_VIDEO 1" >> config_site.h - name: configure - run: CFLAGS="-g -I/usr/local/include -I/usr/local/opt/openssl@1.1/include" LDFLAGS="-L/usr/local/lib -L/usr/local/opt/openssl@1.1/lib" ./configure + run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb)" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" ./configure - name: make run: make - name: disable firewall @@ -183,17 +183,17 @@ jobs: steps: - uses: actions/checkout@v2 - name: install dependencies - run: brew install openssl@1.1 x264 libvpx nasm swig + run: brew install openssl x264 libvpx nasm swig - name: get ffmpeg - run: git clone --single-branch --branch release/4.2 https://github.com/FFmpeg/FFmpeg.git + run: git clone --single-branch --branch release/7.0 https://github.com/FFmpeg/FFmpeg.git - name: configure ffmpeg - run: cd FFmpeg && ./configure --enable-shared --disable-static --enable-gpl --enable-libx264 + run: cd FFmpeg && LDFLAGS="-Wl,-ld_classic" ./configure --enable-shared --disable-static --enable-gpl --enable-libx264 - name: build ffmpeg run: cd FFmpeg && make -j10 && sudo make install - name: config site run: echo -e "#define PJMEDIA_HAS_VIDEO 1\n" > pjlib/include/pj/config_site.h - name: configure - run: CFLAGS="-I/usr/local/include -I/usr/local/opt/openssl@1.1/include -fPIC" LDFLAGS="-L/usr/local/lib -L/usr/local/opt/openssl@1.1/lib" CXXFLAGS="-fPIC" ./configure + run: CFLAGS="$(pkg-config --cflags openssl) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib" CXXFLAGS="-fPIC" ./configure - name: make run: make - name: set up Python @@ -209,11 +209,11 @@ jobs: steps: - uses: actions/checkout@v2 - name: install dependencies - run: brew install openssl@1.1 libvpx swig + run: brew install openssl libvpx swig - name: config site run: echo -e "#define PJMEDIA_HAS_VIDEO 1\n#define PJMEDIA_HAS_VID_TOOLBOX_CODEC 1\n" > pjlib/include/pj/config_site.h - name: configure - run: CFLAGS="-I/usr/local/include -I/usr/local/opt/openssl@1.1/include -fPIC" LDFLAGS="-L/usr/local/lib -L/usr/local/opt/openssl@1.1/lib" CXXFLAGS="-fPIC" ./configure + run: CFLAGS="$(pkg-config --cflags openssl) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib" CXXFLAGS="-fPIC" ./configure - name: make run: make - name: set up Python From 30829f03a7d61902e01597fd5578c54a8e9f1b47 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Wed, 8 May 2024 09:09:21 +0700 Subject: [PATCH 021/491] Update Android JNI audio dev to use 16bit PCM only (#3945) --- .../src/pjmedia-audiodev/android_jni_dev.c | 62 ++++++++++--------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/pjmedia/src/pjmedia-audiodev/android_jni_dev.c b/pjmedia/src/pjmedia-audiodev/android_jni_dev.c index 44da35f9f0..85ea3b3a31 100644 --- a/pjmedia/src/pjmedia-audiodev/android_jni_dev.c +++ b/pjmedia/src/pjmedia-audiodev/android_jni_dev.c @@ -150,9 +150,9 @@ static int AndroidRecorderCallback(void *userData) { struct android_aud_stream *stream = (struct android_aud_stream *)userData; jmethodID read_method=0, record_method=0, stop_method=0; - int size = stream->rec_buf_size; - jbyteArray inputBuffer; - jbyte *buf; + int size = stream->rec_buf_size / 2; + jshortArray inputBuffer; + jshort *buf; JNIEnv *jni_env = 0; pj_bool_t attached = attach_jvm(&jni_env); @@ -166,7 +166,7 @@ static int AndroidRecorderCallback(void *userData) /* Get methods ids */ read_method = (*jni_env)->GetMethodID(jni_env, stream->record_class, - "read", "([BII)I"); + "read", "([SII)I"); record_method = (*jni_env)->GetMethodID(jni_env, stream->record_class, "startRecording", "()V"); stop_method = (*jni_env)->GetMethodID(jni_env, stream->record_class, @@ -177,7 +177,7 @@ static int AndroidRecorderCallback(void *userData) } /* Create a buffer for frames read */ - inputBuffer = (*jni_env)->NewByteArray(jni_env, size); + inputBuffer = (*jni_env)->NewShortArray(jni_env, size); if (inputBuffer == 0) { PJ_LOG(3, (THIS_FILE, "Unable to allocate input buffer")); goto on_return; @@ -190,7 +190,7 @@ static int AndroidRecorderCallback(void *userData) while (!stream->quit_flag) { pjmedia_frame frame; pj_status_t status; - int bytesRead; + int shortRead; if (!stream->running) { (*jni_env)->CallVoidMethod(jni_env, stream->record, stop_method); @@ -200,25 +200,25 @@ static int AndroidRecorderCallback(void *userData) (*jni_env)->CallVoidMethod(jni_env, stream->record, record_method); } - bytesRead = (*jni_env)->CallIntMethod(jni_env, stream->record, + shortRead = (*jni_env)->CallIntMethod(jni_env, stream->record, read_method, inputBuffer, 0, size); - if (bytesRead <= 0 || bytesRead != size) { + if (shortRead <= 0 || shortRead != size) { PJ_LOG (4, (THIS_FILE, "Record thread : error %d reading data", - bytesRead)); + shortRead)); continue; } - buf = (*jni_env)->GetByteArrayElements(jni_env, inputBuffer, 0); + buf = (*jni_env)->GetShortArrayElements(jni_env, inputBuffer, 0); frame.type = PJMEDIA_FRAME_TYPE_AUDIO; - frame.size = size; + frame.size = stream->rec_buf_size; frame.bit_info = 0; frame.buf = (void *)buf; frame.timestamp.u64 = stream->rec_timestamp.u64; status = (*stream->rec_cb)(stream->user_data, &frame); - (*jni_env)->ReleaseByteArrayElements(jni_env, inputBuffer, buf, - JNI_ABORT); + (*jni_env)->ReleaseShortArrayElements(jni_env, inputBuffer, buf, + JNI_ABORT); if (status != PJ_SUCCESS || stream->quit_flag) break; @@ -241,9 +241,9 @@ static int AndroidTrackCallback(void *userData) { struct android_aud_stream *stream = (struct android_aud_stream*) userData; jmethodID write_method=0, play_method=0, stop_method=0, flush_method=0; - int size = stream->play_buf_size; - jbyteArray outputBuffer; - jbyte *buf; + int size = stream->play_buf_size / 2; + jshortArray outputBuffer; + jshort *buf; JNIEnv *jni_env = 0; pj_bool_t attached = attach_jvm(&jni_env); @@ -255,7 +255,7 @@ static int AndroidTrackCallback(void *userData) /* Get methods ids */ write_method = (*jni_env)->GetMethodID(jni_env, stream->track_class, - "write", "([BII)I"); + "write", "([SII)I"); play_method = (*jni_env)->GetMethodID(jni_env, stream->track_class, "play", "()V"); stop_method = (*jni_env)->GetMethodID(jni_env, stream->track_class, @@ -269,12 +269,12 @@ static int AndroidTrackCallback(void *userData) goto on_return; } - outputBuffer = (*jni_env)->NewByteArray(jni_env, size); + outputBuffer = (*jni_env)->NewShortArray(jni_env, size); if (outputBuffer == 0) { PJ_LOG(3, (THIS_FILE, "Unable to allocate output buffer")); goto on_return; } - buf = (*jni_env)->GetByteArrayElements(jni_env, outputBuffer, 0); + buf = (*jni_env)->GetShortArrayElements(jni_env, outputBuffer, 0); /* Start playing */ pj_thread_set_prio(NULL, THREAD_PRIORITY_URGENT_AUDIO); @@ -283,7 +283,7 @@ static int AndroidTrackCallback(void *userData) while (!stream->quit_flag) { pjmedia_frame frame; pj_status_t status; - int bytesWritten; + int shortWritten; if (!stream->running) { (*jni_env)->CallVoidMethod(jni_env, stream->track, stop_method); @@ -295,7 +295,7 @@ static int AndroidTrackCallback(void *userData) } frame.type = PJMEDIA_FRAME_TYPE_AUDIO; - frame.size = size; + frame.size = stream->play_buf_size; frame.buf = (void *)buf; frame.timestamp.u64 = stream->play_timestamp.u64; frame.bit_info = 0; @@ -307,16 +307,16 @@ static int AndroidTrackCallback(void *userData) if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) pj_bzero(frame.buf, frame.size); - (*jni_env)->ReleaseByteArrayElements(jni_env, outputBuffer, buf, + (*jni_env)->ReleaseShortArrayElements(jni_env, outputBuffer, buf, JNI_COMMIT); /* Write to the device output. */ - bytesWritten = (*jni_env)->CallIntMethod(jni_env, stream->track, + shortWritten = (*jni_env)->CallIntMethod(jni_env, stream->track, write_method, outputBuffer, 0, size); - if (bytesWritten <= 0 || bytesWritten != size) { + if (shortWritten <= 0 || shortWritten != size) { PJ_LOG(4, (THIS_FILE, "Player thread: Error %d writing data", - bytesWritten)); + shortWritten)); continue; } @@ -324,7 +324,7 @@ static int AndroidTrackCallback(void *userData) stream->param.channel_count; }; - (*jni_env)->ReleaseByteArrayElements(jni_env, outputBuffer, buf, 0); + (*jni_env)->ReleaseShortArrayElements(jni_env, outputBuffer, buf, 0); (*jni_env)->DeleteLocalRef(jni_env, outputBuffer); on_return: @@ -476,7 +476,8 @@ static pj_status_t android_create_stream(pjmedia_aud_dev_factory *f, PJ_ASSERT_RETURN(param->channel_count >= 1 && param->channel_count <= 2, PJ_EINVAL); - PJ_ASSERT_RETURN(param->bits_per_sample==8 || param->bits_per_sample==16, + PJ_ASSERT_RETURN(/* param->bits_per_sample==8 || */ + param->bits_per_sample==16, PJ_EINVAL); PJ_ASSERT_RETURN(play_cb && rec_cb && p_aud_strm, PJ_EINVAL); @@ -500,8 +501,11 @@ static pj_status_t android_create_stream(pjmedia_aud_dev_factory *f, 12 /*CHANNEL_IN_STEREO*/; channelOutCfg = (param->channel_count == 1)? 4 /*CHANNEL_OUT_MONO*/: 12 /*CHANNEL_OUT_STEREO*/; - sampleFormat = (param->bits_per_sample == 8)? 3 /*ENCODING_PCM_8BIT*/: - 2 /*ENCODING_PCM_16BIT*/; + + // The 8bit/byte read/write methods no longer support 16bit + //sampleFormat = (param->bits_per_sample == 8)? 3 /*ENCODING_PCM_8BIT*/: + // 2 /*ENCODING_PCM_16BIT*/; + sampleFormat = 2 /*ENCODING_PCM_16BIT*/; attached = attach_jvm(&jni_env); From 1dab9b63accb3194d9d6d595b0c02877965131be Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Wed, 8 May 2024 10:25:03 +0700 Subject: [PATCH 022/491] Add TLS/SSL backend: Windows Schannel (#3867) --- .github/workflows/ci-win.yml | 24 +- pjlib/build/pjlib.vcxproj | 3 +- pjlib/include/pj/config.h | 3 + pjlib/include/pj/ssl_sock.h | 82 ++ pjlib/src/pj/activesock.c | 6 +- pjlib/src/pj/ssl_sock_common.c | 4 + pjlib/src/pj/ssl_sock_imp_common.c | 116 +- pjlib/src/pj/ssl_sock_imp_common.h | 6 + pjlib/src/pj/ssl_sock_schannel.c | 1631 +++++++++++++++++++++++ pjlib/src/pjlib-test/ssl_sock.c | 211 ++- pjnath/include/pjnath/turn_sock.h | 8 + pjnath/src/pjnath/turn_sock.c | 9 + pjsip-apps/src/swig/symbols.i | 8 + pjsip-apps/src/swig/symbols.lst | 2 +- pjsip/include/pjsip/sip_transport_tls.h | 11 + pjsip/include/pjsua2/siptypes.hpp | 19 + pjsip/src/pjsip/sip_transport_tls.c | 10 + pjsip/src/pjsua2/siptypes.cpp | 8 + 18 files changed, 2071 insertions(+), 90 deletions(-) create mode 100644 pjlib/src/pj/ssl_sock_schannel.c diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index b6263e721c..bff96d083c 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -178,25 +178,10 @@ jobs: msbuild pjproject-vs14.sln /p:PlatformToolset=v143 /p:Configuration=Release /p:Platform=win32 /p:UseEnv=true shell: cmd - windows-with-video-libvpx-unit-test-1: + windows-with-video-libvpx-schannel-unit-test-1: runs-on: windows-latest steps: - uses: actions/checkout@master - - name: get openssl - run: Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/openssl-1.1.1s-win.zip" -OutFile ".\openssl.zip" - shell: powershell - - name: expand openssl - run: | - Expand-Archive -LiteralPath .\openssl.zip -DestinationPath .; pwd - cd openssl_build - Add-Content ..\openssl_dir.txt $pwd.Path - shell: powershell - - name: check openssl folder - run: | - set /P OPENSSL_DIR= + @@ -998,6 +998,7 @@ + true diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h index 1a234fd8f7..6736d47344 100644 --- a/pjlib/include/pj/config.h +++ b/pjlib/include/pj/config.h @@ -1077,6 +1077,9 @@ /** Using Apple's Network framework */ #define PJ_SSL_SOCK_IMP_APPLE 4 +/** Using Windows's Schannel */ +#define PJ_SSL_SOCK_IMP_SCHANNEL 5 + /** * Select which SSL socket implementation to use. Currently pjlib supports * PJ_SSL_SOCK_IMP_OPENSSL, which uses OpenSSL, and PJ_SSL_SOCK_IMP_GNUTLS, diff --git a/pjlib/include/pj/ssl_sock.h b/pjlib/include/pj/ssl_sock.h index f82953af8c..d24be89211 100644 --- a/pjlib/include/pj/ssl_sock.h +++ b/pjlib/include/pj/ssl_sock.h @@ -117,6 +117,11 @@ typedef enum pj_ssl_cert_verify_flag_t */ PJ_SSL_CERT_ECHAIN_TOO_LONG = (1 << 8), + /** + * The certificate signature is created using a weak hashing algorithm. + */ + PJ_SSL_CERT_EWEAK_SIGNATURE = (1 << 9), + /** * The server identity does not match to any identities specified in * the certificate, e.g: subjectAltName extension, subject common name. @@ -145,6 +150,59 @@ typedef enum pj_ssl_cert_name_type PJ_SSL_CERT_NAME_IP } pj_ssl_cert_name_type; +/** + * Field type for looking up SSL certificate in the certificate stores. + */ +typedef enum pj_ssl_cert_lookup_type +{ + /** + * No certificate to be looked up. + */ + PJ_SSL_CERT_LOOKUP_NONE, + + /** + * Lookup by subject, this will lookup any first certificate whose + * subject containing the specified keyword. Note that subject may not + * be unique in the store, so the lookup may end up selecting a wrong + * certificate. + */ + PJ_SSL_CERT_LOOKUP_SUBJECT, + + /** + * Lookup by fingerprint/thumbprint (SHA1 hash), this will lookup + * any first certificate whose fingerprint matching the specified + * keyword. The keyword is an array of hash octets. + */ + PJ_SSL_CERT_LOOKUP_FINGERPRINT, + + /** + * Lookup by friendly name, this will lookup any first certificate + * whose friendly name containing the specified keyword. Note that + * friendly name may not be unique in the store, so the lookup may end up + * selecting a wrong certificate. + */ + PJ_SSL_CERT_LOOKUP_FRIENDLY_NAME + +} pj_ssl_cert_lookup_type; + +/** + * Describe structure of certificate lookup criteria. + */ +typedef struct pj_ssl_cert_lookup_criteria +{ + /** + * Certificate field type to look. + */ + pj_ssl_cert_lookup_type type; + + /* + * Keyword to match on the field specified in \a type. + */ + pj_str_t keyword; + +} pj_ssl_cert_lookup_criteria; + + /** * Describe structure of certificate info. */ @@ -273,6 +331,30 @@ PJ_DECL(pj_status_t) pj_ssl_cert_load_from_buffer(pj_pool_t *pool, const pj_str_t *privkey_pass, pj_ssl_cert_t **p_cert); +/** + * Create credential from OS certificate store, this function will lookup + * certificate using the specified criterias. + * + * Currently this is used by Windows Schannel backend only, it will lookup + * in the Current User store first, if no certificate with the specified + * criteria is not found, it will lookup in the Local Machine store. + * + * Note that for manual verification (e.g: when pj_ssl_sock_param.verify_peer + * is disabled), the backend will provide pre-verification result against + * trusted CA certificates in Current User store only (will not check CA + * certificates in the Local Machine store). + * + * @param pool The pool. + * @param criteria The lookup criteria. + * @param p_cert Pointer to credential instance to be created. + * + * @return PJ_SUCCESS when successful. + */ +PJ_DECL(pj_status_t) pj_ssl_cert_load_from_store( + pj_pool_t *pool, + const pj_ssl_cert_lookup_criteria *criteria, + pj_ssl_cert_t **p_cert); + /** * Dump SSL certificate info. * diff --git a/pjlib/src/pj/activesock.c b/pjlib/src/pj/activesock.c index a892a658d1..c1c4a09856 100644 --- a/pjlib/src/pj/activesock.c +++ b/pjlib/src/pj/activesock.c @@ -513,7 +513,8 @@ static void ioqueue_on_read_complete(pj_ioqueue_key_t *key, ret = (*asock->cb.on_data_read)(asock, r->pkt, r->size, PJ_SUCCESS, &remainder); PJ_ASSERT_ON_FAIL( - !asock->stream_oriented || remainder <= r->size, { + !ret || !asock->stream_oriented || remainder <= r->size, + { PJ_LOG(2, ("", "App bug! Invalid remainder length from " "activesock on_data_read().")); @@ -589,7 +590,8 @@ static void ioqueue_on_read_complete(pj_ioqueue_key_t *key, ret = (*asock->cb.on_data_read)(asock, r->pkt, r->size, status, &remainder); PJ_ASSERT_ON_FAIL( - !asock->stream_oriented || remainder <= r->size, { + !ret || !asock->stream_oriented || remainder <= r->size, + { PJ_LOG(2, ("", "App bug! Invalid remainder length from " "activesock on_data_read().")); diff --git a/pjlib/src/pj/ssl_sock_common.c b/pjlib/src/pj/ssl_sock_common.c index d741895ea0..65aba8ddab 100644 --- a/pjlib/src/pj/ssl_sock_common.c +++ b/pjlib/src/pj/ssl_sock_common.c @@ -173,6 +173,10 @@ PJ_DEF(pj_status_t) pj_ssl_cert_get_verify_status_strings( case PJ_SSL_CERT_ECHAIN_TOO_LONG: p = "The certificate chain length is too long"; break; + case PJ_SSL_CERT_EWEAK_SIGNATURE: + p = "The certificate signature is created using a weak hashing " + "algorithm"; + break; case PJ_SSL_CERT_EIDENTITY_NOT_MATCH: p = "The server identity does not match to any identities " "specified in the certificate"; diff --git a/pjlib/src/pj/ssl_sock_imp_common.c b/pjlib/src/pj/ssl_sock_imp_common.c index 11e3bd4152..f1d1db63dd 100644 --- a/pjlib/src/pj/ssl_sock_imp_common.c +++ b/pjlib/src/pj/ssl_sock_imp_common.c @@ -52,9 +52,22 @@ static pj_bool_t asock_on_data_sent (pj_activesock_t *asock, ******************************************************************* */ +static pj_size_t next_pow2(pj_size_t n) +{ + /* Next 32-bit power of two */ + pj_size_t power = 1; + while (power < n && power < 0x8000000) { + power <<= 1; + } + return power; +} + static pj_status_t circ_init(pj_pool_factory *factory, circ_buf_t *cb, pj_size_t cap) { + /* Round-up cap */ + cap = next_pow2(cap); + cb->cap = cap; cb->readp = 0; cb->writep = 0; @@ -68,17 +81,24 @@ static pj_status_t circ_init(pj_pool_factory *factory, /* Allocate circular buffer */ cb->buf = pj_pool_alloc(cb->pool, cap); if (!cb->buf) { - pj_pool_release(cb->pool); + pj_pool_secure_release(&cb->pool); return PJ_ENOMEM; } return PJ_SUCCESS; } +static void circ_reset(circ_buf_t* cb) +{ + cb->readp = 0; + cb->writep = 0; + cb->size = 0; +} + static void circ_deinit(circ_buf_t *cb) { if (cb->pool) { - pj_pool_release(cb->pool); + pj_pool_secure_release(&cb->pool); cb->pool = NULL; } } @@ -104,6 +124,8 @@ static void circ_read(circ_buf_t *cb, pj_uint8_t *dst, pj_size_t len) pj_size_t tbc = PJ_MIN(size_after, len); pj_size_t rem = len - tbc; + pj_assert(cb->size >= len); + pj_memcpy(dst, cb->buf + cb->readp, tbc); pj_memcpy(dst + tbc, cb->buf, rem); @@ -113,6 +135,21 @@ static void circ_read(circ_buf_t *cb, pj_uint8_t *dst, pj_size_t len) cb->size -= len; } +/* Cancel previous read, partially or fully. + * Should be called in the same mutex block as circ_read(). + */ +static void circ_read_cancel(circ_buf_t* cb, pj_size_t len) +{ + pj_assert(cb->cap - cb->size >= len); + + if (cb->readp < len) + cb->readp = cb->cap - (len - cb->readp); + else + cb->readp -= len; + + cb->size += len; +} + static pj_status_t circ_write(circ_buf_t *cb, const pj_uint8_t *src, pj_size_t len) { @@ -121,14 +158,8 @@ static pj_status_t circ_write(circ_buf_t *cb, /* Minimum required capacity */ pj_size_t min_cap = len + cb->size; - /* Next 32-bit power of two */ - min_cap--; - min_cap |= min_cap >> 1; - min_cap |= min_cap >> 2; - min_cap |= min_cap >> 4; - min_cap |= min_cap >> 8; - min_cap |= min_cap >> 16; - min_cap++; + /* Round-up minimum capacity */ + min_cap = next_pow2(min_cap); /* Create a new pool to hold a bigger buffer, using the same factory */ pj_pool_t *pool = pj_pool_create(cb->pool->factory, "tls-circ%p", @@ -153,7 +184,7 @@ static pj_status_t circ_write(circ_buf_t *cb, cb->size = old_size; /* Release the previous pool */ - pj_pool_release(cb->pool); + pj_pool_secure_release(&cb->pool); /* Update circular buffer members */ cb->pool = pool; @@ -1737,7 +1768,7 @@ static pj_status_t ssl_send (pj_ssl_sock_t *ssock, unsigned flags) { pj_status_t status; - int nwritten; + int nwritten = 0; /* Write the plain data to SSL, after SSL encrypts it, the buffer will * contain the secured data to be sent via socket. Note that re- @@ -2241,8 +2272,9 @@ static void wipe_buf(pj_str_t *buf) } PJ_DEF(void) pj_ssl_cert_wipe_keys(pj_ssl_cert_t *cert) -{ +{ if (cert) { +#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL) wipe_buf(&cert->CA_file); wipe_buf(&cert->CA_path); wipe_buf(&cert->cert_file); @@ -2251,6 +2283,10 @@ PJ_DEF(void) pj_ssl_cert_wipe_keys(pj_ssl_cert_t *cert) wipe_buf(&cert->CA_buf); wipe_buf(&cert->cert_buf); wipe_buf(&cert->privkey_buf); +#else + cert->criteria.type = PJ_SSL_CERT_LOOKUP_NONE; + wipe_buf(&cert->criteria.keyword); +#endif } } @@ -2274,6 +2310,7 @@ PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files2(pj_pool_t *pool, const pj_str_t *privkey_pass, pj_ssl_cert_t **p_cert) { +#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL) pj_ssl_cert_t *cert; PJ_ASSERT_RETURN(pool && (CA_file || CA_path) && cert_file && @@ -2294,6 +2331,16 @@ PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files2(pj_pool_t *pool, *p_cert = cert; return PJ_SUCCESS; +#else + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(CA_file); + PJ_UNUSED_ARG(CA_path); + PJ_UNUSED_ARG(cert_file); + PJ_UNUSED_ARG(privkey_file); + PJ_UNUSED_ARG(privkey_pass); + PJ_UNUSED_ARG(p_cert); + return PJ_ENOTSUP; +#endif } PJ_DEF(pj_status_t) pj_ssl_cert_load_from_buffer(pj_pool_t *pool, @@ -2303,6 +2350,7 @@ PJ_DEF(pj_status_t) pj_ssl_cert_load_from_buffer(pj_pool_t *pool, const pj_str_t *privkey_pass, pj_ssl_cert_t **p_cert) { +#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL) pj_ssl_cert_t *cert; PJ_ASSERT_RETURN(pool && CA_buf && cert_buf && privkey_buf, PJ_EINVAL); @@ -2316,8 +2364,44 @@ PJ_DEF(pj_status_t) pj_ssl_cert_load_from_buffer(pj_pool_t *pool, *p_cert = cert; return PJ_SUCCESS; +#else + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(CA_buf); + PJ_UNUSED_ARG(cert_buf); + PJ_UNUSED_ARG(privkey_buf); + PJ_UNUSED_ARG(privkey_pass); + PJ_UNUSED_ARG(p_cert); + return PJ_ENOTSUP; +#endif +} + + +PJ_DEF(pj_status_t) pj_ssl_cert_load_from_store( + pj_pool_t *pool, + const pj_ssl_cert_lookup_criteria *criteria, + pj_ssl_cert_t **p_cert) +{ +#if (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_SCHANNEL) + pj_ssl_cert_t *cert; + + PJ_ASSERT_RETURN(pool && criteria && p_cert, PJ_EINVAL); + + cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t); + pj_memcpy(&cert->criteria, criteria, sizeof(*criteria)); + pj_strdup_with_null(pool, &cert->criteria.keyword, &criteria->keyword); + + *p_cert = cert; + + return PJ_SUCCESS; +#else + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(criteria); + PJ_UNUSED_ARG(p_cert); + return PJ_ENOTSUP; +#endif } + /* Set SSL socket credentials. */ PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate( pj_ssl_sock_t *ssock, @@ -2330,6 +2414,8 @@ PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate( cert_ = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t); pj_memcpy(cert_, cert, sizeof(pj_ssl_cert_t)); + +#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL) pj_strdup_with_null(pool, &cert_->CA_file, &cert->CA_file); pj_strdup_with_null(pool, &cert_->CA_path, &cert->CA_path); pj_strdup_with_null(pool, &cert_->cert_file, &cert->cert_file); @@ -2339,6 +2425,10 @@ PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate( pj_strdup(pool, &cert_->CA_buf, &cert->CA_buf); pj_strdup(pool, &cert_->cert_buf, &cert->cert_buf); pj_strdup(pool, &cert_->privkey_buf, &cert->privkey_buf); +#else + pj_strdup_with_null(pool, &cert_->criteria.keyword, + &cert->criteria.keyword); +#endif ssock->cert = cert_; diff --git a/pjlib/src/pj/ssl_sock_imp_common.h b/pjlib/src/pj/ssl_sock_imp_common.h index 14ee122da8..e50dc16dc9 100644 --- a/pjlib/src/pj/ssl_sock_imp_common.h +++ b/pjlib/src/pj/ssl_sock_imp_common.h @@ -152,6 +152,7 @@ struct pj_ssl_sock_t */ struct pj_ssl_cert_t { +#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL) pj_str_t CA_file; pj_str_t CA_path; pj_str_t cert_file; @@ -162,6 +163,9 @@ struct pj_ssl_cert_t pj_ssl_cert_buffer CA_buf; pj_ssl_cert_buffer cert_buf; pj_ssl_cert_buffer privkey_buf; +#else + pj_ssl_cert_lookup_criteria criteria; +#endif }; /* ssl available ciphers */ @@ -205,9 +209,11 @@ static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock); static pj_status_t circ_init(pj_pool_factory *factory, circ_buf_t *cb, pj_size_t cap); static void circ_deinit(circ_buf_t *cb); +static void circ_reset(circ_buf_t* cb); static pj_bool_t circ_empty(const circ_buf_t *cb); static pj_size_t circ_size(const circ_buf_t *cb); static void circ_read(circ_buf_t *cb, pj_uint8_t *dst, pj_size_t len); +static void circ_read_cancel(circ_buf_t* cb, pj_size_t len); static pj_status_t circ_write(circ_buf_t *cb, const pj_uint8_t *src, pj_size_t len); diff --git a/pjlib/src/pj/ssl_sock_schannel.c b/pjlib/src/pj/ssl_sock_schannel.c new file mode 100644 index 0000000000..0341c0fd93 --- /dev/null +++ b/pjlib/src/pj/ssl_sock_schannel.c @@ -0,0 +1,1631 @@ +/* + * Copyright (C) 2024 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Only build when PJ_HAS_SSL_SOCK is enabled and when the backend is + * Schannel. + * + * Note: + * - Older Windows SDK versions are not supported (some APIs deprecated, + * also missing newer/safer TLS protocol versions). + */ +#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \ + (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_SCHANNEL) && \ + defined(_MSC_VER) && _MSC_VER>=1900 + +#define THIS_FILE "ssl_sock_schannel.c" + +/* Sender string in logging */ +#define SENDER "ssl_schannel" + +/* Subject for self-signed certificate */ +#define SELF_SIGNED_CERT_SUBJECT "CN=lab.pjsip.org" + +/* Debugging */ +#define DEBUG_SCHANNEL 0 + +#if DEBUG_SCHANNEL +# define LOG_DEBUG(sender,title) PJ_LOG(4,(sender,title)) +# define LOG_DEBUG1(sender,title,p1) PJ_LOG(4,(sender,title,p1)) +# define LOG_DEBUG2(sender,title,p1,p2) PJ_LOG(4,(sender,title,p1,p2)) +# define LOG_DEBUG3(sender,title,p1,p2,p3) PJ_LOG(4,(sender,title,p1,p2,p3)) +# define LOG_DEBUG_ERR(sender,title,sec_status) \ + log_sec_err(4,sender,title,sec_status) +#else +# define LOG_DEBUG(sender,title) +# define LOG_DEBUG1(sender,title,p1) +# define LOG_DEBUG2(sender,title,p1,p2) +# define LOG_DEBUG3(sender,title,p1,p2,p3) +# define LOG_DEBUG_ERR(sender,title,sec_status) +#endif + + +/* For using SSPI */ +#define SECURITY_WIN32 + +/* For using SCH_CREDENTIALS */ +#define SCHANNEL_USE_BLACKLISTS +#include +#include +#include +#include // for enumerating ciphers + +#pragma comment (lib, "secur32.lib") +#pragma comment (lib, "shlwapi.lib") +#pragma comment (lib, "Crypt32.lib") // for creating & manipulating certs +#pragma comment (lib, "Bcrypt.lib") // for enumerating ciphers + + +/* SSL sock implementation API */ +#define SSL_SOCK_IMP_USE_CIRC_BUF +#include "ssl_sock_imp_common.h" + +#define MIN_READ_BUF_CAP (1024*8) +#define MIN_WRITE_BUF_CAP (1024*8) + + +/* + * Schannel global vars. + */ +static struct sch_ssl_t +{ + pj_caching_pool cp; + pj_pool_t *pool; + unsigned long init_cnt; + PCCERT_CONTEXT self_signed_cert; +} sch_ssl; + + +/* + * Secure socket structure definition. + */ +typedef struct sch_ssl_sock_t +{ + pj_ssl_sock_t base; + + CredHandle cred_handle; + CtxtHandle ctx_handle; + PCCERT_CONTEXT cert_ctx; + SecPkgContext_StreamSizes strm_sizes; + + pj_uint8_t *write_buf; + pj_size_t write_buf_cap; + pj_uint8_t *read_buf; + pj_size_t read_buf_cap; + circ_buf_t decrypted_buf; +} sch_ssl_sock_t; + + +#include "ssl_sock_imp_common.c" + + +/* === Helper functions === */ + +#define SNAME(ssock) ((ssock)->pool->obj_name) +#define PJ_SSL_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*6) + +/* Map SECURITY_STATUS to pj_status_t. + * + * SECURITY_STATUS/Windows-error 32 bit structure (from winerror.h): + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +---+-+-+-----------------------+-------------------------------+ + * |Sev|C|R| Facility | Code | + * +---+-+-+-----------------------+-------------------------------+ + * + * For this mapping, we only save one severity bit & 15 bit error code. + * The facility value for security/SSPI is 9, which needs to be inserted + * when converting back from pj_status_t. + * + * So in pj_status_t it is stored as: + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +---+-+-+-----------------------+-------------------------------+ + * | PJLIB internal | Code |S| + * +---+-+-+-----------------------+-------------------------------+ + */ +static pj_status_t sec_err_to_pj(SECURITY_STATUS ss) +{ + DWORD err = ((ss & 0x7FFF) << 1) | ((ss & 0x80000000) >> 31); + + /* Make sure it does not exceed PJ_ERRNO_SPACE_SIZE */ + if (err >= PJ_ERRNO_SPACE_SIZE) { + LOG_DEBUG1(SENDER,"sec_err_to_pj() failed mapping error code %d", + err); + return PJ_EUNKNOWN; + } + + return PJ_SSL_ERRNO_START + err; +} + +/* Get SECURITY_STATUS from pj_status_t. */ +static SECURITY_STATUS sec_err_from_pj(pj_status_t status) +{ + SECURITY_STATUS ss; + + /* Make sure it is within SSL error space */ + if (status < PJ_SSL_ERRNO_START || + status >= PJ_SSL_ERRNO_START + PJ_ERRNO_SPACE_SIZE) + { + LOG_DEBUG1(SENDER,"sec_err_from_pj() failed mapping status code %d", + status); + return ERROR_INVALID_DATA; + } + + ss = status - PJ_SSL_ERRNO_START; + ss = (ss >> 1) | ((ss & 1) << 31) | 0x00090000; + return ss; +} + +/* Print Schannel error to log */ +static void log_sec_err(int log_level, const char* sender, + const char* title, SECURITY_STATUS ss) +{ + char *str = NULL; + DWORD len; + len = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, ss, 0, (LPSTR)&str, 0, NULL); + /* Trim new line chars */ + while (len > 0 && (str[len-1] == '\r' || str[len-1] == '\n')) + str[--len] = 0; + + switch (log_level) { + case 1: + PJ_LOG(1, (sender, "%s: 0x%x-%s", title, ss, str)); + break; + case 2: + PJ_LOG(2, (sender, "%s: 0x%x-%s", title, ss, str)); + break; + case 3: + PJ_LOG(3, (sender, "%s: 0x%x-%s", title, ss, str)); + break; + case 4: + PJ_LOG(4, (sender, "%s: 0x%x-%s", title, ss, str)); + break; + case 5: + PJ_LOG(5, (sender, "%s: 0x%x-%s", title, ss, str)); + break; + default: + PJ_LOG(6, (sender, "%s: 0x%x-%s", title, ss, str)); + break; + } + + LocalFree(str); +} + +static pj_str_t sch_err_print(pj_status_t e, char *msg, pj_size_t max) +{ + DWORD len; + SECURITY_STATUS ss = sec_err_from_pj(e); + pj_str_t pjstr = {0}; + + len = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, ss, 0, (LPSTR)msg, (DWORD)max, NULL); + + /* Trim new line chars */ + while (len > 0 && (msg[len-1] == '\r' || msg[len-1] == '\n')) + msg[--len] = 0; + + pj_strset(&pjstr, msg, len); + + return pjstr; +} + +static void sch_deinit(void) +{ + if (sch_ssl.self_signed_cert) + CertFreeCertificateContext(sch_ssl.self_signed_cert); + + if (sch_ssl.pool) { + pj_pool_secure_release(&sch_ssl.pool); + pj_caching_pool_destroy(&sch_ssl.cp); + } + + pj_bzero(&sch_ssl, sizeof(sch_ssl)); +} + +static void sch_inc() +{ + if (++sch_ssl.init_cnt == 1 && sch_ssl.pool == NULL) { + pj_caching_pool_init(&sch_ssl.cp, NULL, 0); + sch_ssl.pool = pj_pool_create(&sch_ssl.cp.factory, "sch%p", + 512, 512, NULL); + if (pj_atexit(&sch_deinit) != PJ_SUCCESS) { + PJ_LOG(1,(SENDER, "Failed to register atexit() for Schannel.")); + } + + pj_register_strerror(PJ_SSL_ERRNO_START, PJ_ERRNO_SPACE_SIZE, + &sch_err_print); + } +} + +static void sch_dec() +{ + pj_assert(sch_ssl.init_cnt > 0); + --sch_ssl.init_cnt; +} + + +/* === SSL socket implementations === */ + +/* Allocate SSL backend struct */ +static pj_ssl_sock_t *ssl_alloc(pj_pool_t *pool) +{ + sch_ssl_sock_t *sch_ssock = NULL; + + sch_inc(); + + sch_ssock = (sch_ssl_sock_t*) PJ_POOL_ZALLOC_T(pool, sch_ssl_sock_t); + if (!sch_ssock) + return NULL; + + SecInvalidateHandle(&sch_ssock->cred_handle); + SecInvalidateHandle(&sch_ssock->ctx_handle); + + return &sch_ssock->base; +} + +/* Create and initialize new SSL context and instance */ +static pj_status_t ssl_create(pj_ssl_sock_t *ssock) +{ + sch_ssl_sock_t* sch_ssock = (sch_ssl_sock_t*)ssock; + pj_pool_factory* pf = ssock->pool->factory; + pj_pool_t* pool = ssock->pool; + pj_ssize_t read_cap, write_cap; + pj_status_t status = PJ_SUCCESS; + + /* Ciphers & curves settings should be set via OS/registry */ + if (ssock->param.ciphers_num || ssock->param.curves_num) { + PJ_LOG(3,(SNAME(ssock), "Ciphers and curves settings are ignored, " + "they should be set via OS/registry")); + } + + read_cap = PJ_MAX(MIN_READ_BUF_CAP, ssock->param.read_buffer_size); + write_cap = PJ_MAX(MIN_WRITE_BUF_CAP, ssock->param.send_buffer_size); + + /* Allocate read buffer */ + sch_ssock->read_buf_cap = read_cap; + sch_ssock->read_buf = (pj_uint8_t*)pj_pool_zalloc(pool, read_cap); + if (!sch_ssock->read_buf) { + status = PJ_ENOMEM; + goto on_return; + } + + /* Allocate write buffer */ + sch_ssock->write_buf_cap = write_cap; + sch_ssock->write_buf = (pj_uint8_t*)pj_pool_zalloc(pool, write_cap); + if (!sch_ssock->write_buf) { + status = PJ_ENOMEM; + goto on_return; + } + + /* Initialize input circular buffer */ + status = circ_init(pf, &ssock->circ_buf_input, read_cap); + if (status != PJ_SUCCESS) + goto on_return; + + /* Initialize output circular buffer */ + status = circ_init(pf, &ssock->circ_buf_output, write_cap); + if (status != PJ_SUCCESS) + goto on_return; + + /* Initialize decrypted circular buffer */ + status = circ_init(pf, &sch_ssock->decrypted_buf, read_cap); + if (status != PJ_SUCCESS) + goto on_return; + +on_return: + if (status != PJ_SUCCESS) { + PJ_PERROR(1, (SENDER, status, "Error allocating SSL")); + } + + return status; +} + + +/* Destroy SSL context and instance */ +static void ssl_destroy(pj_ssl_sock_t* ssock) +{ + sch_ssl_sock_t* sch_ssock = (sch_ssl_sock_t*)ssock; + + /* Destroy circular buffers */ + circ_deinit(&ssock->circ_buf_input); + circ_deinit(&ssock->circ_buf_output); + circ_deinit(&sch_ssock->decrypted_buf); + + /* Free certificate */ + if (sch_ssock->cert_ctx) { + CertFreeCertificateContext(sch_ssock->cert_ctx); + sch_ssock->cert_ctx = NULL; + } + + sch_dec(); +} + +/* Reset SSL socket state */ +static void ssl_reset_sock_state(pj_ssl_sock_t* ssock) +{ + sch_ssl_sock_t* sch_ssock = (sch_ssl_sock_t*)ssock; + SECURITY_STATUS ss; + + LOG_DEBUG(SNAME(ssock), "SSL reset"); + + pj_lock_acquire(ssock->write_mutex); + + /* Signal shutdown only when SSL connection has been established */ + if (ssock->ssl_state == SSL_STATE_ESTABLISHED && + SecIsValidHandle(&sch_ssock->ctx_handle)) + { + DWORD type = SCHANNEL_SHUTDOWN; + + SecBuffer buf_in[1] = { {0} }; + buf_in[0].BufferType = SECBUFFER_TOKEN; + buf_in[0].pvBuffer = &type; + buf_in[0].cbBuffer = sizeof(type); + SecBufferDesc buf_desc_in = { 0 }; + buf_desc_in.ulVersion = SECBUFFER_VERSION; + buf_desc_in.cBuffers = ARRAYSIZE(buf_in); + buf_desc_in.pBuffers = buf_in; + ApplyControlToken(&sch_ssock->ctx_handle, &buf_desc_in); + + SecBuffer buf_out[1] = { {0} }; + buf_out[0].BufferType = SECBUFFER_TOKEN; + buf_out[0].pvBuffer = sch_ssock->write_buf; + buf_out[0].cbBuffer = (ULONG)sch_ssock->write_buf_cap; + SecBufferDesc buf_desc_out = { 0 }; + buf_desc_out.ulVersion = SECBUFFER_VERSION; + buf_desc_out.cBuffers = ARRAYSIZE(buf_out); + buf_desc_out.pBuffers = buf_out; + + DWORD flags = + ISC_REQ_CONFIDENTIALITY | + ISC_REQ_REPLAY_DETECT | + ISC_REQ_SEQUENCE_DETECT | + ISC_REQ_STREAM; + ss = InitializeSecurityContext(&sch_ssock->cred_handle, + &sch_ssock->ctx_handle, + NULL, + flags, 0, 0, + &buf_desc_out, 0, NULL, + &buf_desc_out, + &flags, NULL); + if (ss == SEC_E_OK) { + /* May need to send TLS shutdown packets */ + if (buf_out->cbBuffer > 0 && buf_out[0].pvBuffer) { + pj_status_t status; + + status = circ_write(&ssock->circ_buf_output, + buf_out[0].pvBuffer, + buf_out[0].cbBuffer); + if (status != PJ_SUCCESS) { + PJ_PERROR(1, (SNAME(ssock), status, + "Failed to queuehandshake packets")); + } else { + flush_circ_buf_output(ssock, &ssock->shutdown_op_key, + 0, 0); + } + } + } else { + log_sec_err(1, SNAME(ssock), "Error in shutting down SSL", ss); + } + } + + ssock->ssl_state = SSL_STATE_NULL; + + if (SecIsValidHandle(&sch_ssock->ctx_handle)) { + DeleteSecurityContext(&sch_ssock->ctx_handle); + SecInvalidateHandle(&sch_ssock->ctx_handle); + } + + if (SecIsValidHandle(&sch_ssock->cred_handle)) { + FreeCredentialsHandle(&sch_ssock->cred_handle); + SecInvalidateHandle(&sch_ssock->cred_handle); + } + circ_reset(&ssock->circ_buf_input); + circ_reset(&ssock->circ_buf_output); + circ_reset(&sch_ssock->decrypted_buf); + + pj_lock_release(ssock->write_mutex); + + ssl_close_sockets(ssock); +} + +/* Ciphers and certs */ +static void ssl_ciphers_populate() +{ + PCRYPT_CONTEXT_FUNCTIONS fn = NULL; + ULONG size = 0; + NTSTATUS s; + + /* Populate once only */ + if (ssl_cipher_num) + return; + + s = BCryptEnumContextFunctions(CRYPT_LOCAL, L"SSL", + NCRYPT_SCHANNEL_INTERFACE, + &size, &fn); + if (s < 0) { + PJ_LOG(1,(SENDER, "Error in enumerating ciphers (code=0x%x)", s)); + return; + } + + /* Make sure schannel's pool is created */ + sch_inc(); + sch_dec(); + + for (ULONG i = 0; i < fn->cFunctions; i++) { + char tmp_buf[SZ_ALG_MAX_SIZE]; + pj_str_t tmp_st; + + pj_unicode_to_ansi(fn->rgpszFunctions[i], SZ_ALG_MAX_SIZE, + tmp_buf, sizeof(tmp_buf)); + pj_strdup2_with_null(sch_ssl.pool, &tmp_st, tmp_buf); + + /* Unfortunately we do not get the ID here. + * Let's just set ID to (0x8000000 + i) for now. + */ + ssl_ciphers[ssl_cipher_num].id = 0x8000000 + i; + ssl_ciphers[ssl_cipher_num].name = tmp_st.ptr; + ++ssl_cipher_num; + } + + BCryptFreeBuffer(fn); +} + +static pj_ssl_cipher ssl_get_cipher(pj_ssl_sock_t *ssock) +{ + sch_ssl_sock_t* sch_ssock = (sch_ssl_sock_t*)ssock; + SecPkgContext_CipherInfo ci; + SECURITY_STATUS ss; + + if (!SecIsValidHandle(&sch_ssock->ctx_handle)) { + return PJ_TLS_UNKNOWN_CIPHER; + } + + ss = QueryContextAttributes(&sch_ssock->ctx_handle, + SECPKG_ATTR_CIPHER_INFO, + &ci); + if (ss == SEC_E_OK) { + pj_ssl_cipher c = (pj_ssl_cipher)ci.dwCipherSuite; + + /* Check if this is in the cipher list */ + if (ssl_cipher_num < PJ_SSL_SOCK_MAX_CIPHERS && + !pj_ssl_cipher_name(c)) + { + char tmp_buf[SZ_ALG_MAX_SIZE+1]; + unsigned i; + + pj_unicode_to_ansi(ci.szCipherSuite, SZ_ALG_MAX_SIZE, + tmp_buf, sizeof(tmp_buf)); + + /* If cipher is actually in the list and: + * - if ID is 0, update it, or + * - if ID does not match, return ID from our list. + */ + for (i = 0; i < ssl_cipher_num; ++i) { + if (!pj_ansi_stricmp(ssl_ciphers[i].name, tmp_buf)) { + if (ssl_ciphers[i].id == 0) + ssl_ciphers[i].id = c; + else + c = ssl_ciphers[i].id; + break; + } + } + + /* Add to cipher list if not found */ + if (i == ssl_cipher_num) { + pj_str_t tmp_st; + pj_strdup2_with_null(sch_ssl.pool, &tmp_st, tmp_buf); + + ssl_ciphers[ssl_cipher_num].id = c; + ssl_ciphers[ssl_cipher_num].name = tmp_st.ptr; + ++ssl_cipher_num; + } + } + return c; + } + + return PJ_TLS_UNKNOWN_CIPHER; +} + +static pj_status_t blob_to_str(DWORD enc_type, CERT_NAME_BLOB* blob, + DWORD flag, char *buf, unsigned buf_len) +{ + DWORD ret; + ret = CertNameToStrA(enc_type, blob, flag, buf, buf_len); + if (ret < 0) { + PJ_LOG(3,(SENDER, "Failed to convert cert blob to string")); + return PJ_ETOOSMALL; + } + return PJ_SUCCESS; +} + + +static pj_status_t file_time_to_time_val(const FILETIME* file_time, + pj_time_val* time_val) +{ + FILETIME local_file_time; + SYSTEMTIME localTime; + pj_parsed_time pt; + + if (!FileTimeToLocalFileTime(file_time, &local_file_time)) + return PJ_RETURN_OS_ERROR(GetLastError()); + + if (!FileTimeToSystemTime(&local_file_time, &localTime)) + return PJ_RETURN_OS_ERROR(GetLastError()); + + pj_bzero(&pt, sizeof(pt)); + pt.year = localTime.wYear; + pt.mon = localTime.wMonth - 1; + pt.day = localTime.wDay; + pt.wday = localTime.wDayOfWeek; + + pt.hour = localTime.wHour; + pt.min = localTime.wMinute; + pt.sec = localTime.wSecond; + pt.msec = localTime.wMilliseconds; + + return pj_time_encode(&pt, time_val); +} + + +static void cert_parse_info(pj_pool_t* pool, pj_ssl_cert_info* ci, + const CERT_CONTEXT *cert) +{ + PCERT_INFO cert_info = cert->pCertInfo; + char buf[512]; + pj_uint8_t serial_no[20]; + unsigned serial_size; + pj_bool_t update_needed; + pj_status_t status; + + /* Get issuer & serial no first */ + status = blob_to_str(cert->dwCertEncodingType, &cert_info->Issuer, + CERT_SIMPLE_NAME_STR, + buf, sizeof(buf)); + + serial_size = PJ_MIN(cert_info->SerialNumber.cbData, sizeof(serial_no)); + pj_memcpy(&serial_no, cert_info->SerialNumber.pbData, serial_size); + + /* Check if the contents need to be updated */ + update_needed = status == PJ_SUCCESS && + (pj_strcmp2(&ci->issuer.info, buf) || + pj_memcmp(ci->serial_no, serial_no, serial_size)); + if (!update_needed) + return; + + /* Update info */ + + pj_bzero(ci, sizeof(pj_ssl_cert_info)); + + /* Version */ + ci->version = cert_info->dwVersion; + + /* Issuer */ + pj_strdup2(pool, &ci->issuer.info, buf); + status = blob_to_str(cert->dwCertEncodingType, &cert_info->Issuer, + CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG, + buf, sizeof(buf)); + if (status == PJ_SUCCESS) + pj_strdup2(pool, &ci->issuer.cn, buf); + + /* Serial number */ + pj_memcpy(ci->serial_no, serial_no, serial_size); + + /* Subject */ + status = blob_to_str(cert->dwCertEncodingType, &cert_info->Subject, + CERT_SIMPLE_NAME_STR, + buf, sizeof(buf)); + if (status == PJ_SUCCESS) + pj_strdup2(pool, &ci->subject.info, buf); + + status = blob_to_str(cert->dwCertEncodingType, &cert_info->Subject, + CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG, + buf, sizeof(buf)); + if (status == PJ_SUCCESS) + pj_strdup2(pool, &ci->subject.cn, buf); + + /* Validity */ + file_time_to_time_val(&cert_info->NotAfter, &ci->validity.end); + file_time_to_time_val(&cert_info->NotBefore, &ci->validity.start); + ci->validity.gmt = 0; + + /* Subject Alternative Name extension */ + while (1) { + PCERT_EXTENSION ext = CertFindExtension(szOID_SUBJECT_ALT_NAME2, + cert_info->cExtension, + cert_info->rgExtension); + if (!ext) + break; + + CERT_ALT_NAME_INFO* alt_name_info = NULL; + DWORD alt_name_info_size = 0; + BOOL rv; + rv = CryptDecodeObjectEx(cert->dwCertEncodingType, + szOID_SUBJECT_ALT_NAME2, + ext->Value.pbData, + ext->Value.cbData, + CRYPT_DECODE_ALLOC_FLAG | + CRYPT_DECODE_NOCOPY_FLAG, + NULL, + &alt_name_info, + &alt_name_info_size); + if (!rv) + break; + + ci->subj_alt_name.entry = pj_pool_calloc( + pool, alt_name_info->cAltEntry, + sizeof(*ci->subj_alt_name.entry)); + if (!ci->subj_alt_name.entry) { + PJ_LOG(3,(SENDER, "Failed to allocate memory for SubjectAltName")); + LocalFree(alt_name_info); + break; + } + + for (unsigned i = 0; i < alt_name_info->cAltEntry; ++i) { + CERT_ALT_NAME_ENTRY *ane = &alt_name_info->rgAltEntry[i]; + pj_ssl_cert_name_type type; + unsigned len = 0; + + switch (ane->dwAltNameChoice) { + case CERT_ALT_NAME_DNS_NAME: + type = PJ_SSL_CERT_NAME_DNS; + len = pj_unicode_to_ansi(ane->pwszDNSName, sizeof(buf), + buf, sizeof(buf)) != NULL; + break; + case CERT_ALT_NAME_IP_ADDRESS: + type = PJ_SSL_CERT_NAME_IP; + pj_inet_ntop2(ane->IPAddress.cbData == sizeof(pj_in6_addr)? + pj_AF_INET6() : pj_AF_INET(), + ane->IPAddress.pbData, buf, sizeof(buf)); + break; + case CERT_ALT_NAME_URL: + type = PJ_SSL_CERT_NAME_URI; + len = pj_unicode_to_ansi(ane->pwszDNSName, sizeof(buf), + buf, sizeof(buf)) != NULL; + break; + case CERT_ALT_NAME_RFC822_NAME: + type = PJ_SSL_CERT_NAME_RFC822; + len = pj_unicode_to_ansi(ane->pwszDNSName, sizeof(buf), + buf, sizeof(buf)) != NULL; + break; + default: + type = PJ_SSL_CERT_NAME_UNKNOWN; + break; + } + + if (len && type != PJ_SSL_CERT_NAME_UNKNOWN) { + ci->subj_alt_name.entry[ci->subj_alt_name.cnt].type = type; + pj_strdup2(pool, + &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name, + buf); + ci->subj_alt_name.cnt++; + } + } + + /* Done parsing Subject Alt Name */ + LocalFree(alt_name_info); + break; + } +} + +static void ssl_update_certs_info(pj_ssl_sock_t *ssock) +{ + sch_ssl_sock_t* sch_ssock = (sch_ssl_sock_t*)ssock; + CERT_CONTEXT *cert_ctx = NULL; + SECURITY_STATUS ss; + + if (!SecIsValidHandle(&sch_ssock->ctx_handle)) { + return; + } + + ss = QueryContextAttributes(&sch_ssock->ctx_handle, + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + &cert_ctx); + + if (ss != SEC_E_OK || !cert_ctx) { + if (!ssock->is_server) + log_sec_err(1, SNAME(ssock), + "Failed to retrieve remote certificate", ss); + } else { + cert_parse_info(ssock->pool, &ssock->remote_cert_info, cert_ctx); + CertFreeCertificateContext(cert_ctx); + } + + ss = QueryContextAttributes(&sch_ssock->ctx_handle, + SECPKG_ATTR_LOCAL_CERT_CONTEXT, + &cert_ctx); + + if (ss != SEC_E_OK || !cert_ctx) { + if (ssock->is_server) + log_sec_err(3, SNAME(ssock), + "Failed to retrieve local certificate", ss); + } + else { + cert_parse_info(ssock->pool, &ssock->local_cert_info, cert_ctx); + CertFreeCertificateContext(cert_ctx); + } +} + +/* SSL session functions */ +static void ssl_set_state(pj_ssl_sock_t* ssock, pj_bool_t is_server) +{ + /* Nothing to do */ + PJ_UNUSED_ARG(ssock); + PJ_UNUSED_ARG(is_server); +} + +static void ssl_set_peer_name(pj_ssl_sock_t* ssock) +{ + /* Nothing to do */ + PJ_UNUSED_ARG(ssock); +} + + +static PCCERT_CONTEXT create_self_signed_cert() +{ + if (!sch_ssl.self_signed_cert) { + CERT_EXTENSIONS cert_ext = { 0 }; + BYTE cert_name_buffer[64]; + CERT_NAME_BLOB cert_name; + cert_name.pbData = cert_name_buffer; + cert_name.cbData = ARRAYSIZE(cert_name_buffer); + + if (!CertStrToNameA(X509_ASN_ENCODING, SELF_SIGNED_CERT_SUBJECT, + CERT_X500_NAME_STR, NULL, + cert_name.pbData, &cert_name.cbData, NULL)) + { + return NULL; + } + + sch_ssl.self_signed_cert = CertCreateSelfSignCertificate( + 0, &cert_name, 0, NULL, + NULL, NULL, NULL, + &cert_ext); + } + + /* May return NULL on any failure */ + return CertDuplicateCertificateContext(sch_ssl.self_signed_cert); +} + +static PCCERT_CONTEXT find_cert_in_stores(pj_ssl_cert_lookup_type type, + const pj_str_t *keyword) +{ + PCCERT_CONTEXT cert = NULL; + + LOG_DEBUG3(SENDER, + "Looking up certificate with criteria: type=%d keyword=%.*s", + type, (type==PJ_SSL_CERT_LOOKUP_FINGERPRINT? 4:keyword->slen), + (type==PJ_SSL_CERT_LOOKUP_FINGERPRINT?"[..]":keyword->ptr)); + + /* Find in Current User & Local Machine stores */ + DWORD flags[2] = { CERT_SYSTEM_STORE_CURRENT_USER, + CERT_SYSTEM_STORE_LOCAL_MACHINE }; + + for (int i = 0; (!cert && i < PJ_ARRAY_SIZE(flags)); ++i) { + HCERTSTORE store = NULL; + + /* Open the store */ + store = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING, + 0, flags[i], L"MY"); + if (!store) { + log_sec_err(1, SENDER, "Error opening store", GetLastError()); + continue; + } + + LOG_DEBUG1(SENDER, "Looking up certificate in store: %s", + (i==0? "Current User" : "Local Machine")); + + /* Lookup based on type */ + + if (type == PJ_SSL_CERT_LOOKUP_SUBJECT) { + cert = CertFindCertificateInStore( + store, X509_ASN_ENCODING, 0, + CERT_FIND_SUBJECT_STR_A, keyword->ptr, NULL); + + } else if (type == PJ_SSL_CERT_LOOKUP_FINGERPRINT) { + CRYPT_HASH_BLOB hash = {0}; + hash.cbData = (DWORD)keyword->slen; + hash.pbData = (BYTE*)keyword->ptr; + cert = CertFindCertificateInStore( + store, X509_ASN_ENCODING, + 0, CERT_FIND_SHA1_HASH, &hash, NULL); + + } else if (type == PJ_SSL_CERT_LOOKUP_FRIENDLY_NAME) { + WCHAR buf[256]; + DWORD buf_size; + + if (keyword->slen >= sizeof(buf)) { + PJ_LOG(1,(SENDER,"Cannot lookup certificate, friendly name " + "keyword is too long (max=%d)",sizeof(buf))); + } else { + cert = NULL; + while (1) { + cert = CertEnumCertificatesInStore(store, cert); + if (!cert) + break; + + buf_size = sizeof(buf); + if (CertGetCertificateContextProperty( + cert, CERT_FRIENDLY_NAME_PROP_ID, buf, &buf_size)) + { + char buf2[256]; + pj_ssize_t buf2_len; + + /* The output buf is null-terminated */ + pj_unicode_to_ansi(buf, -1, buf2, sizeof(buf2)); + buf2_len = pj_ansi_strlen(buf2); + if (keyword->slen == buf2_len && + !pj_memcmp(buf2, keyword->ptr, buf2_len)) + { + /* Found it */ + break; + } + } + } + } + } + + CertCloseStore(store, 0); + } + + if (!cert) { + PJ_LOG(1,(SENDER, + "Cannot find certificate with criteria: " + "type=%d keyword=%.*s", type, + (type==PJ_SSL_CERT_LOOKUP_FINGERPRINT? 4:keyword->slen), + (type==PJ_SSL_CERT_LOOKUP_FINGERPRINT?"[..]":keyword->ptr))); + } + return cert; +} + + +/* Initialize credentials */ +static pj_status_t init_creds(pj_ssl_sock_t* ssock) +{ + sch_ssl_sock_t* sch_ssock = (sch_ssl_sock_t*)ssock; + SCH_CREDENTIALS creds = { 0 }; + unsigned param_cnt = 0; + TLS_PARAMETERS param = {0}; + SECURITY_STATUS ss; + + creds.dwVersion = SCH_CREDENTIALS_VERSION; + creds.dwFlags = SCH_USE_STRONG_CRYPTO; + + /* Setup Protocol version */ + if (ssock->param.proto != PJ_SSL_SOCK_PROTO_DEFAULT && + ssock->param.proto != PJ_SSL_SOCK_PROTO_ALL) + { + DWORD tmp = 0; + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_3) + tmp |= SP_PROT_TLS1_3; + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_2) + tmp |= SP_PROT_TLS1_2; + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_1) + tmp |= SP_PROT_TLS1_1; + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1) + tmp |= SP_PROT_TLS1_0; + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_SSL3) + tmp |= SP_PROT_SSL3; + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_SSL2) + tmp |= SP_PROT_SSL2; + if (tmp) { + param_cnt = 1; + param.grbitDisabledProtocols = ~tmp; + LOG_DEBUG1(SNAME(ssock), "grbitDisabledProtocols=0x%x", (~tmp)); + } + } + + /* Setup the TLS certificate */ + if (ssock->cert && + ssock->cert->criteria.type != PJ_SSL_CERT_LOOKUP_NONE && + !sch_ssock->cert_ctx) + { + /* Search certificate from stores */ + sch_ssock->cert_ctx = + find_cert_in_stores(ssock->cert->criteria.type, + &ssock->cert->criteria.keyword); + } + + if (ssock->is_server) { + if (!ssock->cert || + ssock->cert->criteria.type == PJ_SSL_CERT_LOOKUP_NONE) + { + /* No certificate specified, use self-signed cert */ + sch_ssock->cert_ctx = create_self_signed_cert(); + PJ_LOG(2,(SNAME(ssock), + "Warning: certificate is not specified for " + "TLS server, using a self-signed certificate.")); + } + } else { + creds.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; + } + + if (sch_ssock->cert_ctx) { + creds.cCreds = 1; + creds.paCred = &sch_ssock->cert_ctx; + } + + /* Verification */ + if (!ssock->is_server) { + if (ssock->param.verify_peer) + creds.dwFlags |= SCH_CRED_AUTO_CRED_VALIDATION | + SCH_CRED_REVOCATION_CHECK_CHAIN; + else + creds.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION; + } else { + if (ssock->param.verify_peer) + creds.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN; + } + + /* Now init the credentials */ + if (param_cnt) { + creds.cTlsParameters = param_cnt; + creds.pTlsParameters = ¶m; + } + + ss = AcquireCredentialsHandle(NULL, UNISP_NAME, + ssock->is_server? SECPKG_CRED_INBOUND : + SECPKG_CRED_OUTBOUND, + NULL, &creds, NULL, NULL, + &sch_ssock->cred_handle, NULL); + if (ss < 0) { + log_sec_err(1, SNAME(ssock), + "Failed in AcquireCredentialsHandle()", ss); + + return sec_err_to_pj(ss); + } + + return PJ_SUCCESS; +} + +/* Initialize credentials using older data type SCHANNEL_CRED */ +static pj_status_t init_creds_old(pj_ssl_sock_t* ssock) +{ + sch_ssl_sock_t* sch_ssock = (sch_ssl_sock_t*)ssock; + SCHANNEL_CRED creds = { 0 }; + SECURITY_STATUS ss; + + creds.dwVersion = SCHANNEL_CRED_VERSION; + creds.dwFlags = SCH_USE_STRONG_CRYPTO; + + /* Setup Protocol version */ + if (ssock->param.proto != PJ_SSL_SOCK_PROTO_DEFAULT && + ssock->param.proto != PJ_SSL_SOCK_PROTO_ALL) + { + DWORD tmp = 0; + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_3) + tmp |= SP_PROT_TLS1_3; + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_2) + tmp |= SP_PROT_TLS1_2; + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_1) + tmp |= SP_PROT_TLS1_1; + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1) + tmp |= SP_PROT_TLS1_0; + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_SSL3) + tmp |= SP_PROT_SSL3; + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_SSL2) + tmp |= SP_PROT_SSL2; + if (tmp) { + creds.grbitEnabledProtocols = tmp; + LOG_DEBUG1(SNAME(ssock), "grbitEnabledProtocols=0x%x", tmp); + } + } + + /* Setup the TLS certificate */ + if (ssock->cert && + ssock->cert->criteria.type != PJ_SSL_CERT_LOOKUP_NONE && + !sch_ssock->cert_ctx) + { + /* Search certificate from stores */ + sch_ssock->cert_ctx = + find_cert_in_stores(ssock->cert->criteria.type, + &ssock->cert->criteria.keyword); + } + + if (ssock->is_server) { + if (!ssock->cert || + ssock->cert->criteria.type == PJ_SSL_CERT_LOOKUP_NONE) + { + /* No certificate specified, use self-signed cert */ + sch_ssock->cert_ctx = create_self_signed_cert(); + PJ_LOG(2,(SNAME(ssock), + "Warning: TLS server does not specify a " + "certificate, use a self-signed certificate")); + } + } else { + creds.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; + } + + if (sch_ssock->cert_ctx) { + creds.cCreds = 1; + creds.paCred = &sch_ssock->cert_ctx; + } + + /* Verification */ + if (!ssock->is_server) { + if (ssock->param.verify_peer) + creds.dwFlags |= SCH_CRED_AUTO_CRED_VALIDATION | + SCH_CRED_REVOCATION_CHECK_CHAIN; + else + creds.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION; + } else { + if (ssock->param.verify_peer) + creds.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN; + } + + ss = AcquireCredentialsHandle(NULL, UNISP_NAME, + ssock->is_server? SECPKG_CRED_INBOUND : + SECPKG_CRED_OUTBOUND, + NULL, &creds, NULL, NULL, + &sch_ssock->cred_handle, NULL); + if (ss < 0) { + log_sec_err(1, SNAME(ssock), + "Failed in AcquireCredentialsHandle()", ss); + + return sec_err_to_pj(ss); + } + + return PJ_SUCCESS; +} + + + +static void verify_remote_cert(pj_ssl_sock_t* ssock) +{ + sch_ssl_sock_t* sch_ssock = (sch_ssl_sock_t*)ssock; + CERT_CONTEXT *cert_ctx = NULL; + CERT_CHAIN_CONTEXT *chain_ctx = NULL; + CERT_CHAIN_PARA chain_para = {0}; + DWORD info, err; + SECURITY_STATUS ss; + + ss = QueryContextAttributes(&sch_ssock->ctx_handle, + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + &cert_ctx); + if (ss != SEC_E_OK || !cert_ctx) { + if (!ssock->is_server || + (ssock->is_server && ssock->param.require_client_cert)) + { + log_sec_err(1, SNAME(ssock), "Error querying remote cert", ss); + } + goto on_return; + } + + chain_para.cbSize = sizeof(chain_para); + chain_para.cbSize = sizeof(chain_para); + if (!CertGetCertificateChain(HCCE_CURRENT_USER, + (PCCERT_CONTEXT)cert_ctx, + NULL, NULL, &chain_para, 0, 0, + &chain_ctx)) + { + log_sec_err(1, SNAME(ssock), + "Failed to get remote cert chain for verification", + GetLastError()); + goto on_return; + } + + info = chain_ctx->TrustStatus.dwInfoStatus; + err = chain_ctx->TrustStatus.dwErrorStatus; + + if (err == CERT_TRUST_NO_ERROR) { + ssock->verify_status = PJ_SUCCESS; + return; + } + + if (err & CERT_TRUST_IS_NOT_TIME_VALID) + ssock->verify_status |= PJ_SSL_CERT_EVALIDITY_PERIOD; + if (err & CERT_TRUST_IS_REVOKED) + ssock->verify_status |= PJ_SSL_CERT_EREVOKED; + if (err & CERT_TRUST_IS_NOT_SIGNATURE_VALID) + ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED; + if (err & CERT_TRUST_IS_NOT_VALID_FOR_USAGE) + ssock->verify_status |= PJ_SSL_CERT_EINVALID_PURPOSE; + if (err & CERT_TRUST_IS_UNTRUSTED_ROOT) + ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED; + if (err & CERT_TRUST_REVOCATION_STATUS_UNKNOWN) + ssock->verify_status |= PJ_SSL_CERT_ECRL_FAILURE; + if (err & CERT_TRUST_IS_CYCLIC) + ssock->verify_status |= PJ_SSL_CERT_ECHAIN_TOO_LONG; + if (err & CERT_TRUST_INVALID_EXTENSION || + err & CERT_TRUST_INVALID_POLICY_CONSTRAINTS || + err & CERT_TRUST_INVALID_BASIC_CONSTRAINTS || + err & CERT_TRUST_INVALID_NAME_CONSTRAINTS || + err & CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT || + err & CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT || + err & CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT || + err & CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT) + { + ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT; + } + if (err & CERT_TRUST_IS_OFFLINE_REVOCATION) + ssock->verify_status |= PJ_SSL_CERT_ECRL_FAILURE; + if (err & CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY) + ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT; + if (err & CERT_TRUST_IS_EXPLICIT_DISTRUST) + ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED; + if (err & CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT) + ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT; + if (err & CERT_TRUST_HAS_WEAK_SIGNATURE) + ssock->verify_status |= PJ_SSL_CERT_EWEAK_SIGNATURE; + + if (err & CERT_TRUST_IS_PARTIAL_CHAIN) + ssock->verify_status |= PJ_SSL_CERT_ECHAIN_TOO_LONG; + if (err & CERT_TRUST_CTL_IS_NOT_TIME_VALID) + ssock->verify_status |= PJ_SSL_CERT_EVALIDITY_PERIOD; + if (err & CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID) + ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED; + if (err & CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE) + ssock->verify_status |= PJ_SSL_CERT_EINVALID_PURPOSE; + + /* Some unknown error */ + if (ssock->verify_status == PJ_SUCCESS) + ssock->verify_status = PJ_SSL_CERT_EUNKNOWN; + +on_return: + if (chain_ctx) + CertFreeCertificateChain(chain_ctx); + if (cert_ctx) + CertFreeCertificateContext(cert_ctx); +} + + +static pj_status_t ssl_do_handshake(pj_ssl_sock_t* ssock) +{ + sch_ssl_sock_t* sch_ssock = (sch_ssl_sock_t*)ssock; + pj_bool_t renego_req; + pj_size_t data_in_size = 0; + pj_uint8_t* data_in = NULL; + SECURITY_STATUS ss; + pj_status_t status = PJ_EPENDING; + pj_status_t status2; + + pj_lock_acquire(ssock->write_mutex); + + /* Create credential handle, if not yet */ + if (!SecIsValidHandle(&sch_ssock->cred_handle)) { + status2 = init_creds(ssock); + if (status2 != PJ_SUCCESS) { + /* On error, retry using older version of credential */ + status2 = init_creds_old(ssock); + } + if (status2 != PJ_SUCCESS) { + status = status2; + goto on_return; + } + } + + /* Is this a renegotiation request? */ + renego_req = (ssock->ssl_state == SSL_STATE_ESTABLISHED); + + /* Start handshake iteration */ + + pj_lock_acquire(ssock->circ_buf_input_mutex); + + if (!circ_empty(&ssock->circ_buf_input) && !renego_req) { + data_in = sch_ssock->read_buf; + data_in_size = PJ_MIN(sch_ssock->read_buf_cap, + circ_size(&ssock->circ_buf_input)); + circ_read(&ssock->circ_buf_input, data_in, data_in_size); + } + + SecBuffer buf_in[2] = { {0} }; + buf_in[0].BufferType = SECBUFFER_TOKEN; + buf_in[0].pvBuffer = data_in; + buf_in[0].cbBuffer = (ULONG)data_in_size; + buf_in[1].BufferType = SECBUFFER_EMPTY; + SecBufferDesc buf_desc_in = { 0 }; + buf_desc_in.ulVersion = SECBUFFER_VERSION; + buf_desc_in.cBuffers = ARRAYSIZE(buf_in); + buf_desc_in.pBuffers = buf_in; + + SecBuffer buf_out[1] = { {0} }; + buf_out[0].BufferType = SECBUFFER_TOKEN; + buf_out[0].pvBuffer = sch_ssock->write_buf; + buf_out[0].cbBuffer = (ULONG)sch_ssock->write_buf_cap; + SecBufferDesc buf_desc_out = { 0 }; + buf_desc_out.ulVersion = SECBUFFER_VERSION; + buf_desc_out.cBuffers = ARRAYSIZE(buf_out); + buf_desc_out.pBuffers = buf_out; + + /* As client */ + if (!ssock->is_server) { + DWORD flags = + ISC_REQ_USE_SUPPLIED_CREDS | + ISC_REQ_CONFIDENTIALITY | + ISC_REQ_REPLAY_DETECT | + ISC_REQ_SEQUENCE_DETECT | + ISC_REQ_STREAM; + + ss = InitializeSecurityContext( + &sch_ssock->cred_handle, + SecIsValidHandle(&sch_ssock->ctx_handle)? + &sch_ssock->ctx_handle : NULL, + (SEC_CHAR*)ssock->param.server_name.ptr, + flags, 0, 0, + data_in_size? &buf_desc_in : NULL, + 0, + SecIsValidHandle(&sch_ssock->ctx_handle)? + NULL : &sch_ssock->ctx_handle, + &buf_desc_out, &flags, NULL); + } + + /* As server */ + else { + DWORD flags = + ASC_REQ_CONFIDENTIALITY | + ASC_REQ_REPLAY_DETECT | + ASC_REQ_SEQUENCE_DETECT | + ASC_REQ_STREAM; + + if (ssock->param.require_client_cert) + flags |= ASC_REQ_MUTUAL_AUTH; + + ss = AcceptSecurityContext( + &sch_ssock->cred_handle, + SecIsValidHandle(&sch_ssock->ctx_handle) ? + &sch_ssock->ctx_handle : NULL, + data_in_size ? &buf_desc_in : NULL, + flags, 0, + SecIsValidHandle(&sch_ssock->ctx_handle) ? + NULL : &sch_ssock->ctx_handle, + &buf_desc_out, &flags, NULL); + } + + /* Check for any unprocessed input data, put it back to buffer */ + if (buf_in[1].BufferType==SECBUFFER_EXTRA && buf_in[1].cbBuffer>0) { + circ_read_cancel(&ssock->circ_buf_input, buf_in[1].cbBuffer); + } + + if (ss == SEC_E_OK && !renego_req) { + SECURITY_STATUS ss2; + + /* Handshake completed! */ + ssock->ssl_state = SSL_STATE_ESTABLISHED; + status = PJ_SUCCESS; + PJ_LOG(3, (SNAME(ssock), "TLS handshake completed!")); + + /* Get stream sizes */ + ss2 = QueryContextAttributes(&sch_ssock->ctx_handle, + SECPKG_ATTR_STREAM_SIZES, + &sch_ssock->strm_sizes); + if (ss2 != SEC_E_OK) { + log_sec_err(1, SNAME(ssock), "Failed to query stream sizes", ss2); + ssl_reset_sock_state(ssock); + status = sec_err_to_pj(ss2); + } + + /* Adjust maximum message size to our allocated buffer size */ + if (!sch_ssock->write_buf) { + pj_size_t max_msg = sch_ssock->write_buf_cap - + sch_ssock->strm_sizes.cbHeader - + sch_ssock->strm_sizes.cbTrailer; + + sch_ssock->strm_sizes.cbMaximumMessage = + PJ_MIN((ULONG)max_msg, + sch_ssock->strm_sizes.cbMaximumMessage); + } + + /* Manually verify remote cert */ + if (!ssock->param.verify_peer) + verify_remote_cert(ssock); + } + + else if (ss == SEC_I_COMPLETE_NEEDED || + ss == SEC_I_COMPLETE_AND_CONTINUE) + { + /* Perhaps CompleteAuthToken() is unnecessary for Schannel, but + * the sample code seems to call it. + */ + LOG_DEBUG_ERR(SNAME(ssock), "Handshake progress", ss); + ss = CompleteAuthToken(&sch_ssock->ctx_handle, &buf_desc_out); + if (ss != SEC_E_OK) { + log_sec_err(1, SNAME(ssock), + "Handshake error in CompleteAuthToken()", ss); + status = sec_err_to_pj(ss); + } + } + + else if (ss == SEC_I_CONTINUE_NEEDED) + { + LOG_DEBUG_ERR(SNAME(ssock), "Handshake progress", ss); + } + + else if (ss == SEC_E_INCOMPLETE_MESSAGE) + { + LOG_DEBUG_ERR(SNAME(ssock), "Handshake progress", ss); + + /* Put back the incomplete message */ + circ_read_cancel(&ssock->circ_buf_input, data_in_size); + } + + else if (!renego_req) { + /* Handshake failed */ + log_sec_err(1, SNAME(ssock), "Handshake failed!", ss); + status = sec_err_to_pj(ss); + } + + pj_lock_release(ssock->circ_buf_input_mutex); + + if ((ss == SEC_E_OK || ss == SEC_I_CONTINUE_NEEDED) && + buf_out[0].cbBuffer > 0 && buf_out[0].pvBuffer) + { + /* Queue output data to send */ + status2 = circ_write(&ssock->circ_buf_output, buf_out[0].pvBuffer, + buf_out[0].cbBuffer); + if (status2 != PJ_SUCCESS) { + PJ_PERROR(1,(SNAME(ssock), status2, + "Failed to queue handshake packets")); + status = status2; + } + } + + /* Send handshake packets to wire */ + status2 = flush_circ_buf_output(ssock, &ssock->handshake_op_key, 0, 0); + if (status2 != PJ_SUCCESS && status2 != PJ_EPENDING) { + PJ_PERROR(1,(SNAME(ssock), status2, + "Failed to send handshake packets")); + status = status2; + } + +on_return: + pj_lock_release(ssock->write_mutex); + + return status; +} + +static pj_status_t ssl_renegotiate(pj_ssl_sock_t *ssock) +{ + PJ_LOG(3, (SNAME(ssock), "App requested renegotiation..")); + + /* Nothing to do, SSL sock common will invoke ssl_do_handshake() */ + return PJ_SUCCESS; +} + +static int find_sec_buffer(const SecBuffer* buf, int buf_cnt, + unsigned long sec_type) +{ + for (int i = 0; i < buf_cnt; ++i) + if (buf[i].BufferType == sec_type) + return i; + return -1; +} + +static pj_status_t ssl_read(pj_ssl_sock_t* ssock, void* data, int* size) +{ + sch_ssl_sock_t* sch_ssock = (sch_ssl_sock_t*)ssock; + pj_size_t size_ = 0; + pj_uint8_t* data_ = NULL; + SECURITY_STATUS ss; + pj_status_t status = PJ_SUCCESS; + int i, need = *size, requested = *size; + + /* Avoid compile warning of unused debugging var */ + PJ_UNUSED_ARG(requested); + + pj_lock_acquire(ssock->circ_buf_input_mutex); + + /* Try read from the decrypted buffer */ + size_ = circ_size(&sch_ssock->decrypted_buf); + if (need <= size_) { + /* Got all from the decrypted buffer */ + circ_read(&sch_ssock->decrypted_buf, data, need); + *size = need; + LOG_DEBUG1(SNAME(ssock), + "Read %d: returned all from decrypted buffer.", requested); + pj_lock_release(ssock->circ_buf_input_mutex); + return PJ_SUCCESS; + } + + /* Get all data of the decrypted buffer, then decrypt more */ + LOG_DEBUG2(SNAME(ssock), + "Read %d: %d from decrypted buffer..", requested, size_); + circ_read(&sch_ssock->decrypted_buf, data, size_); + *size = (int)size_; + need -= (int)size_; + + /* Decrypt data of network input buffer */ + if (!circ_empty(&ssock->circ_buf_input)) { + data_ = sch_ssock->read_buf; + size_ = PJ_MIN(sch_ssock->read_buf_cap, + circ_size(&ssock->circ_buf_input)); + circ_read(&ssock->circ_buf_input, data_, size_); + } else { + LOG_DEBUG2(SNAME(ssock), "Read %d: no data to decrypt, returned %d.", + requested, *size); + pj_lock_release(ssock->circ_buf_input_mutex); + return PJ_SUCCESS; + } + + SecBuffer buf[4] = { {0} }; + buf[0].BufferType = SECBUFFER_DATA; + buf[0].pvBuffer = data_; + buf[0].cbBuffer = (ULONG)size_; + buf[1].BufferType = SECBUFFER_EMPTY; + buf[2].BufferType = SECBUFFER_EMPTY; + buf[3].BufferType = SECBUFFER_EMPTY; + SecBufferDesc buf_desc = { 0 }; + buf_desc.ulVersion = SECBUFFER_VERSION; + buf_desc.cBuffers = ARRAYSIZE(buf); + buf_desc.pBuffers = buf; + + ss = DecryptMessage(&sch_ssock->ctx_handle, &buf_desc, 0, NULL); + + if (ss == SEC_E_OK) { + /* Check for any unprocessed input data, put it back to buffer */ + i = find_sec_buffer(buf, ARRAYSIZE(buf), SECBUFFER_EXTRA); + if (i >= 0) { + circ_read_cancel(&ssock->circ_buf_input, buf[i].cbBuffer); + } + + /* Process any decrypted data */ + i = find_sec_buffer(buf, ARRAYSIZE(buf), SECBUFFER_DATA); + if (i >= 0) { + pj_uint8_t *p = buf[i].pvBuffer; + pj_size_t len = buf[i].cbBuffer; + if (need <= len) { + /* All requested fulfilled, may have excess */ + pj_memcpy((pj_uint8_t*)data + *size, p, need); + *size += need; + len -= need; + p += need; + + /* Store any excess in the decrypted buffer */ + if (len) + circ_write(&sch_ssock->decrypted_buf, p, len); + + LOG_DEBUG2(SNAME(ssock), "Read %d: after decrypt, excess=%d", + requested, len); + } else { + /* Not enough, just give everything */ + pj_memcpy((pj_uint8_t*)data + *size, p, len); + *size += (int)len; + LOG_DEBUG2(SNAME(ssock),"Read %d: after decrypt, only got %d", + requested, len); + } + } + } + + else if (ss == SEC_E_INCOMPLETE_MESSAGE) + { + /* Put back the incomplete message */ + circ_read_cancel(&ssock->circ_buf_input, size_); + } + + else if (ss == SEC_I_RENEGOTIATE) { + /* Proceed renegotiation (initiated by local or remote) */ + PJ_LOG(3, (SNAME(ssock), "Renegotiation on progress")); + + /* Check for any token for renegotiation */ + i = find_sec_buffer(buf, ARRAYSIZE(buf), SECBUFFER_EXTRA); + if (i >= 0 && buf[i].pvBuffer && buf[i].cbBuffer) { + /* Queue the token as input in the handshake */ + circ_write(&ssock->circ_buf_input, buf[i].pvBuffer, + buf[i].cbBuffer); + } + + /* Set SSL state as handshaking & reset handshake status */ + ssock->ssl_state = SSL_STATE_HANDSHAKING; + ssock->handshake_status = PJ_EUNKNOWN; + status = PJ_EEOF; + } + + else if (ss == SEC_I_CONTEXT_EXPIRED) + { + PJ_LOG(3, (SNAME(ssock), "TLS connection closed")); + //status = sec_err_to_pj(ss); + status = PJ_ECANCELLED; + } + + else { + log_sec_err(1, SNAME(ssock), "Decrypt error", ss); + status = sec_err_to_pj(ss); + } + + pj_lock_release(ssock->circ_buf_input_mutex); + + LOG_DEBUG2(SNAME(ssock), "Read %d: returned=%d.", requested, *size); + return status; +} + + +static pj_status_t ssl_write(pj_ssl_sock_t* ssock, const void* data, + pj_ssize_t size, int* nwritten) +{ + sch_ssl_sock_t* sch_ssock = (sch_ssl_sock_t*)ssock; + pj_ssize_t total = 0; + pj_status_t status = PJ_SUCCESS; + + pj_lock_acquire(ssock->write_mutex); + + while (total < size) { + SECURITY_STATUS ss; + pj_uint8_t *p_header, *p_data, *p_trailer; + pj_ssize_t write_len, out_size; + + write_len = PJ_MIN(size-total, sch_ssock->strm_sizes.cbMaximumMessage); + p_header = sch_ssock->write_buf; + p_data = p_header + sch_ssock->strm_sizes.cbHeader; + p_trailer = p_data + write_len; + + pj_memcpy(p_data, (pj_uint8_t*)data + total, write_len); + + SecBuffer buf[4] = { {0} }; + buf[0].BufferType = SECBUFFER_STREAM_HEADER; + buf[0].pvBuffer = p_header; + buf[0].cbBuffer = sch_ssock->strm_sizes.cbHeader; + buf[1].BufferType = SECBUFFER_DATA; + buf[1].pvBuffer = p_data; + buf[1].cbBuffer = (ULONG)write_len; + buf[2].BufferType = SECBUFFER_STREAM_TRAILER; + buf[2].pvBuffer = p_trailer; + buf[2].cbBuffer = sch_ssock->strm_sizes.cbTrailer; + buf[3].BufferType = SECBUFFER_EMPTY; + + SecBufferDesc buf_desc = { 0 }; + buf_desc.ulVersion = SECBUFFER_VERSION; + buf_desc.cBuffers = ARRAYSIZE(buf); + buf_desc.pBuffers = buf; + + ss = EncryptMessage(&sch_ssock->ctx_handle, 0, &buf_desc, 0); + + if (ss != SEC_E_OK) { + log_sec_err(1, SNAME(ssock), "Encrypt error", ss); + status = sec_err_to_pj(ss); + break; + } + + out_size = (pj_ssize_t)buf[0].cbBuffer + buf[1].cbBuffer + + buf[2].cbBuffer; + status = circ_write(&ssock->circ_buf_output, sch_ssock->write_buf, + out_size); + if (status != PJ_SUCCESS) { + PJ_PERROR(1, (SNAME(ssock), status, + "Failed to queue outgoing packets")); + break; + } + + total += write_len; + } + + pj_lock_release(ssock->write_mutex); + + *nwritten = (int)total; + + return status; +} + + +#endif /* PJ_HAS_SSL_SOCK */ diff --git a/pjlib/src/pjlib-test/ssl_sock.c b/pjlib/src/pjlib-test/ssl_sock.c index 39ec158b0a..a4e8570fea 100644 --- a/pjlib/src/pjlib-test/ssl_sock.c +++ b/pjlib/src/pjlib-test/ssl_sock.c @@ -540,6 +540,39 @@ static pj_status_t load_cert_to_buf(pj_pool_t *pool, const pj_str_t *file_name, } #endif +static pj_status_t load_cert_from_store(pj_pool_t *pool, + pj_ssl_cert_t **p_cert) +{ +#if 0 + /* To test loading certificate from the store, follow these steps: + * 1. Install the certificate & the private-key pair to the store, + * and optionally set the friendly-name for it. + * 2. Update the lookup criteria below (field & keyword). + */ + pj_ssl_cert_lookup_criteria crit = {0}; + + /* Lookup by subject */ + crit.type = PJ_SSL_CERT_LOOKUP_SUBJECT; + pj_cstr(&crit.keyword, "test.pjsip.org"); + + /* Lookup by friendly-name */ + //crit.type = PJ_SSL_CERT_LOOKUP_FRIENDLY_NAME; + //pj_cstr(&crit.keyword, "schannel-test"); + + /* Lookup by fingerprint */ + //crit.type = PJ_SSL_CERT_LOOKUP_FINGERPRINT; + //pj_cstr(&crit.keyword, "\x08\x3a\x6c\xdc\xd0\x19\x59\xec\x28\xc3" + // "\x81\xb8\xc0\x21\x09\xe9\xd5\xf6\x57\x7d"); + + return pj_ssl_cert_load_from_store(pool, &crit, p_cert); +#else + /* Set no certificate, Schannel will use self-signed cert */ + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(p_cert); + return PJ_ENOTFOUND; +#endif +} + static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto, pj_ssl_cipher srv_cipher, pj_ssl_cipher cli_cipher, pj_bool_t req_client_cert, pj_bool_t client_provide_cert) @@ -602,6 +635,19 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto, } /* Set server cert */ +#if (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_SCHANNEL) + /* Schannel backend currently can only load certificates from + * OS cert store. If the certificate loading fails, we'll skip setting + * certificate, so the SSL socket will create & use a self-signed cert. + */ + status = load_cert_from_store(pool, &cert); + if (status == PJ_SUCCESS) { + status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert); + if (status != PJ_SUCCESS) { + goto on_return; + } + } +#else { pj_str_t ca_file = pj_str(CERT_CA_FILE); pj_str_t cert_file = pj_str(CERT_FILE); @@ -643,6 +689,7 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto, goto on_return; } } +#endif status = pj_ssl_sock_start_accept(ssock_serv, pool, &addr, pj_sockaddr_get_len(&addr)); if (status != PJ_SUCCESS) { @@ -686,9 +733,22 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto, goto on_return; } - /* Set cert for client */ - { + /* Set cert for client. + * Reusing certificate for server above, but if client_provide_cert + * is not set, override the certificate with CA certificate. + */ +#if (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_SCHANNEL) + if (client_provide_cert) { + status = load_cert_from_store(pool, &cert); + if (status == PJ_SUCCESS) + status = pj_ssl_sock_set_certificate(ssock_cli, pool, cert); + if (status != PJ_SUCCESS) { + goto on_return; + } + } +#else + { if (!client_provide_cert) { pj_str_t ca_file = pj_str(CERT_CA_FILE); pj_str_t null_str = pj_str(""); @@ -720,6 +780,7 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto, goto on_return; } } +#endif status = pj_ssl_sock_start_connect(ssock_cli, pool, &addr, &listen_addr, pj_sockaddr_get_len(&addr)); if (status == PJ_SUCCESS) { @@ -926,7 +987,41 @@ static int client_non_ssl(unsigned ms_timeout) goto on_return; } + pj_ssl_sock_param_default(¶m); + param.cb.on_accept_complete2 = &ssl_on_accept_complete; + param.cb.on_data_read = &ssl_on_data_read; + param.cb.on_data_sent = &ssl_on_data_sent; + param.ioqueue = ioqueue; + param.timer_heap = timer; + param.timeout.sec = 0; + param.timeout.msec = ms_timeout; + pj_time_val_normalize(¶m.timeout); + + /* SERVER */ + param.user_data = &state_serv; + state_serv.pool = pool; + state_serv.is_server = PJ_TRUE; + state_serv.is_verbose = PJ_TRUE; + + status = pj_ssl_sock_create(pool, ¶m, &ssock_serv); + if (status != PJ_SUCCESS) { + goto on_return; + } + /* Set cert */ +#if (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_SCHANNEL) + /* Schannel backend currently can only load certificates from + * OS cert store. If the certificate loading fails, we'll skip setting + * certificate, so the SSL socket will create & use a self-signed cert. + */ + status = load_cert_from_store(pool, &cert); + if (status == PJ_SUCCESS) { + status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert); + if (status != PJ_SUCCESS) { + goto on_return; + } + } +#else { pj_str_t ca_file = pj_str(CERT_CA_FILE); pj_str_t cert_file = pj_str(CERT_FILE); @@ -964,31 +1059,11 @@ static int client_non_ssl(unsigned ms_timeout) } } - pj_ssl_sock_param_default(¶m); - param.cb.on_accept_complete2 = &ssl_on_accept_complete; - param.cb.on_data_read = &ssl_on_data_read; - param.cb.on_data_sent = &ssl_on_data_sent; - param.ioqueue = ioqueue; - param.timer_heap = timer; - param.timeout.sec = 0; - param.timeout.msec = ms_timeout; - pj_time_val_normalize(¶m.timeout); - - /* SERVER */ - param.user_data = &state_serv; - state_serv.pool = pool; - state_serv.is_server = PJ_TRUE; - state_serv.is_verbose = PJ_TRUE; - - status = pj_ssl_sock_create(pool, ¶m, &ssock_serv); - if (status != PJ_SUCCESS) { - goto on_return; - } - status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert); if (status != PJ_SUCCESS) { goto on_return; } +#endif /* Init bind address */ { @@ -1261,7 +1336,49 @@ static int perf_test(unsigned clients, unsigned ms_handshake_timeout) goto on_return; } + pj_ssl_sock_param_default(¶m); + param.cb.on_accept_complete2 = &ssl_on_accept_complete; + 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.timer_heap = timer; + param.timeout.sec = 0; + param.timeout.msec = ms_handshake_timeout; + pj_time_val_normalize(¶m.timeout); + + /* Init default bind address */ + { + pj_str_t tmp_st; + pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0); + } + + /* SERVER */ + param.user_data = &state_serv; + + state_serv.pool = pool; + state_serv.echo = PJ_TRUE; + state_serv.is_server = PJ_TRUE; + + status = pj_ssl_sock_create(pool, ¶m, &ssock_serv); + if (status != PJ_SUCCESS) { + goto on_return; + } + /* Set cert */ +#if (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_SCHANNEL) + /* Schannel backend currently can only load certificates from + * OS cert store. If the certificate loading fails, we'll skip setting + * certificate, so the SSL socket will create & use a self-signed cert. + */ + status = load_cert_from_store(pool, &cert); + if (status == PJ_SUCCESS) { + status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert); + if (status != PJ_SUCCESS) { + goto on_return; + } + } +#else { pj_str_t ca_file = pj_str(CERT_CA_FILE); pj_str_t cert_file = pj_str(CERT_FILE); @@ -1299,39 +1416,11 @@ static int perf_test(unsigned clients, unsigned ms_handshake_timeout) } } - pj_ssl_sock_param_default(¶m); - param.cb.on_accept_complete2 = &ssl_on_accept_complete; - 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.timer_heap = timer; - param.timeout.sec = 0; - param.timeout.msec = ms_handshake_timeout; - pj_time_val_normalize(¶m.timeout); - - /* Init default bind address */ - { - pj_str_t tmp_st; - pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0); - } - - /* SERVER */ - param.user_data = &state_serv; - - state_serv.pool = pool; - state_serv.echo = PJ_TRUE; - state_serv.is_server = PJ_TRUE; - - status = pj_ssl_sock_create(pool, ¶m, &ssock_serv); - if (status != PJ_SUCCESS) { - goto on_return; - } - status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert); if (status != PJ_SUCCESS) { goto on_return; } +#endif status = pj_ssl_sock_start_accept(ssock_serv, pool, &addr, pj_sockaddr_get_len(&addr)); if (status != PJ_SUCCESS) { @@ -1535,6 +1624,15 @@ int ssl_sock_test(void) * which require SSL server, for now. */ + /* Schannel backend notes: + * - currently it does not support ciphers settings, so we exclude tests + * whose ciphers setting is specified. + * - TLS protocol older than 1.0 is not supported, TLS 1.0 & 1.1 will be + * disabled soon, TLS 1.3 is supported since Windows 11, so for now + * we only include tests with TLS 1.2. + */ + +#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL) PJ_LOG(3,("", "..echo test w/ TLSv1 and PJ_TLS_RSA_WITH_AES_256_CBC_SHA cipher")); ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1, PJ_SSL_SOCK_PROTO_TLS1, PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA, @@ -1548,6 +1646,7 @@ int ssl_sock_test(void) PJ_FALSE, PJ_FALSE); if (ret != 0) return ret; +#endif PJ_LOG(3,("", "..echo test w/ compatible proto: server TLSv1.2 vs client TLSv1.2")); ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1_2, PJ_SSL_SOCK_PROTO_TLS1_2, @@ -1556,6 +1655,7 @@ int ssl_sock_test(void) if (ret != 0) return ret; +#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL) PJ_LOG(3,("", "..echo test w/ compatible proto: server TLSv1.2+1.3 vs client TLSv1.3")); ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1_2 | PJ_SSL_SOCK_PROTO_TLS1_3, PJ_SSL_SOCK_PROTO_TLS1_3, -1, -1, @@ -1569,9 +1669,10 @@ int ssl_sock_test(void) PJ_FALSE, PJ_FALSE); if (ret == 0) return PJ_EBUG; +#endif /* We can't set min/max proto for TLS protocol higher than 1.0. */ -#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_DARWIN) +#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_DARWIN && PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL) PJ_LOG(3,("", "..echo test w/ incompatible proto: server TLSv1.2 vs client TLSv1.3")); ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1_2, PJ_SSL_SOCK_PROTO_TLS1_3, -1, -1, @@ -1584,7 +1685,7 @@ int ssl_sock_test(void) * deprecated and we only have sec_protocol_options_append_tls_ciphersuite(), * but there's no API to remove certain or all ciphers. */ -#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_APPLE) +#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_APPLE && PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL) PJ_LOG(3,("", "..echo test w/ incompatible ciphers")); ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT, PJ_TLS_RSA_WITH_DES_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA, @@ -1593,6 +1694,7 @@ int ssl_sock_test(void) return PJ_EBUG; #endif +#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL) PJ_LOG(3,("", "..echo test w/ client cert required but not provided")); ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT, PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA, @@ -1606,6 +1708,7 @@ int ssl_sock_test(void) PJ_TRUE, PJ_TRUE); if (ret != 0) return ret; +#endif #if WITH_BENCHMARK PJ_LOG(3,("", "..performance test")); diff --git a/pjnath/include/pjnath/turn_sock.h b/pjnath/include/pjnath/turn_sock.h index 3169c2c538..2de6b42670 100644 --- a/pjnath/include/pjnath/turn_sock.h +++ b/pjnath/include/pjnath/turn_sock.h @@ -223,6 +223,14 @@ typedef struct pj_turn_sock_tls_cfg */ pj_str_t password; + /** + * Lookup certificate from OS certificate store with specified criteria. + * + * Currently only used by TLS backend Windows Schannel, please check + * pj_ssl_cert_load_from_store() for more info. + */ + pj_ssl_cert_lookup_criteria cert_lookup; + /** * The ssl socket parameter. * These fields are used by TURN TLS: diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c index e5b73d4299..e273e6e283 100644 --- a/pjnath/src/pjnath/turn_sock.c +++ b/pjnath/src/pjnath/turn_sock.c @@ -1389,12 +1389,21 @@ static void turn_on_state(pj_turn_session *sess, &turn_sock->setting.tls_cfg.privkey_buf, &turn_sock->setting.tls_cfg.password, &turn_sock->cert); + } else if (turn_sock->setting.tls_cfg.cert_lookup.type != + PJ_SSL_CERT_LOOKUP_NONE && + turn_sock->setting.tls_cfg.cert_lookup.keyword.slen) + { + status = pj_ssl_cert_load_from_store( + turn_sock->pool, + &turn_sock->setting.tls_cfg.cert_lookup, + &turn_sock->cert); } if (status != PJ_SUCCESS) { turn_sock_destroy(turn_sock, status); pj_grp_lock_release(turn_sock->grp_lock); return; } + if (turn_sock->cert) { pj_turn_sock_tls_cfg_wipe_keys(&turn_sock->setting.tls_cfg); } diff --git a/pjsip-apps/src/swig/symbols.i b/pjsip-apps/src/swig/symbols.i index bf0965b0d7..5d49c4c06e 100644 --- a/pjsip-apps/src/swig/symbols.i +++ b/pjsip-apps/src/swig/symbols.i @@ -184,6 +184,14 @@ typedef enum pj_ssl_cert_verify_flag_t PJ_SSL_CERT_EUNKNOWN = 1 << 31 } pj_ssl_cert_verify_flag_t; +typedef enum pj_ssl_cert_lookup_type +{ + PJ_SSL_CERT_LOOKUP_NONE, + PJ_SSL_CERT_LOOKUP_SUBJECT, + PJ_SSL_CERT_LOOKUP_FINGERPRINT, + PJ_SSL_CERT_LOOKUP_FRIENDLY_NAME +} pj_ssl_cert_lookup_type; + typedef enum pj_ice_sess_trickle { PJ_ICE_SESS_TRICKLE_DISABLED, diff --git a/pjsip-apps/src/swig/symbols.lst b/pjsip-apps/src/swig/symbols.lst index 6e2f4257e7..3170fc5a1b 100644 --- a/pjsip-apps/src/swig/symbols.lst +++ b/pjsip-apps/src/swig/symbols.lst @@ -2,7 +2,7 @@ pj/types.h pj_status_t pj_constants_ pj_uint8_t pj_int32_t pj_uint32_t pj_uint pj/file_io.h pj_file_access pj/log.h pj_log_decoration pj/sock_qos.h pj_qos_type pj_qos_flag pj_qos_wmm_prio pj_qos_params -pj/ssl_sock.h pj_ssl_cipher pj_ssl_sock_proto pj_ssl_cert_name_type pj_ssl_cert_verify_flag_t +pj/ssl_sock.h pj_ssl_cipher pj_ssl_sock_proto pj_ssl_cert_name_type pj_ssl_cert_verify_flag_t pj_ssl_cert_lookup_type pjnath/ice_session.h pj_ice_sess_trickle pjnath/nat_detect.h pj_stun_nat_type diff --git a/pjsip/include/pjsip/sip_transport_tls.h b/pjsip/include/pjsip/sip_transport_tls.h index c902765eeb..ce9aab95ca 100644 --- a/pjsip/include/pjsip/sip_transport_tls.h +++ b/pjsip/include/pjsip/sip_transport_tls.h @@ -188,6 +188,14 @@ typedef struct pjsip_tls_setting */ pj_ssl_cert_buffer privkey_buf; + /** + * Lookup certificate from OS certificate store with specified criteria. + * + * Currently only used by TLS backend Windows Schannel, please check + * pj_ssl_cert_load_from_store() for more info. + */ + pj_ssl_cert_lookup_criteria cert_lookup; + /** * Password to open private key. */ @@ -474,6 +482,9 @@ PJ_INLINE(void) pjsip_tls_setting_copy(pj_pool_t *pool, pj_strdup(pool, &dst->cert_buf, &src->cert_buf); pj_strdup(pool, &dst->privkey_buf, &src->privkey_buf); + pj_strdup_with_null(pool, &dst->cert_lookup.keyword, + &src->cert_lookup.keyword); + if (src->ciphers_num) { unsigned i; dst->ciphers = (pj_ssl_cipher*) pj_pool_calloc(pool, src->ciphers_num, diff --git a/pjsip/include/pjsua2/siptypes.hpp b/pjsip/include/pjsua2/siptypes.hpp index 3ad30007f0..7018f404e1 100644 --- a/pjsip/include/pjsua2/siptypes.hpp +++ b/pjsip/include/pjsua2/siptypes.hpp @@ -174,6 +174,25 @@ struct TlsConfig : public PersistentObject */ string privKeyBuf; + /** + * Lookup certificate from OS certificate store, this setting will + * specify the field type to lookup. + * + * Currently only used by Windows Schannel backend, see also + * \a pj_ssl_cert_load_from_store() for more info. + */ + pj_ssl_cert_lookup_type certLookupType; + + /** + * Lookup certificate from OS certificate store, this setting will + * specify the keyword to match on the field specified in + * \a certLookupType above. + * + * Currently only used by Windows Schannel backend, see also + * \a pj_ssl_cert_load_from_store() for more info. + */ + string certLookupKeyword; + /** * TLS protocol method from #pjsip_ssl_method. In the future, this field * might be deprecated in favor of proto field. For now, this field diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c index 5b5cd02b27..6a724e3d4e 100644 --- a/pjsip/src/pjsip/sip_transport_tls.c +++ b/pjsip/src/pjsip/sip_transport_tls.c @@ -647,6 +647,16 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt, &listener->cert); if (status != PJ_SUCCESS) goto on_error; + } else if (listener->tls_setting.cert_lookup.type != + PJ_SSL_CERT_LOOKUP_NONE && + listener->tls_setting.cert_lookup.keyword.slen) + { + status = pj_ssl_cert_load_from_store( + pool, + &listener->tls_setting.cert_lookup, + &listener->cert); + if (status != PJ_SUCCESS) + goto on_error; } /* Register to transport manager */ diff --git a/pjsip/src/pjsua2/siptypes.cpp b/pjsip/src/pjsua2/siptypes.cpp index 331e3245b8..dd5716c5d4 100644 --- a/pjsip/src/pjsua2/siptypes.cpp +++ b/pjsip/src/pjsua2/siptypes.cpp @@ -192,6 +192,8 @@ pjsip_tls_setting TlsConfig::toPj() const ts.ca_buf = str2Pj(this->CaBuf); ts.cert_buf = str2Pj(this->certBuf); ts.privkey_buf = str2Pj(this->privKeyBuf); + ts.cert_lookup.type = this->certLookupType; + ts.cert_lookup.keyword = str2Pj(this->certLookupKeyword); ts.method = this->method; ts.ciphers_num = (unsigned)this->ciphers.size(); ts.proto = this->proto; @@ -221,6 +223,8 @@ void TlsConfig::fromPj(const pjsip_tls_setting &prm) this->CaBuf = pj2Str(prm.ca_buf); this->certBuf = pj2Str(prm.cert_buf); this->privKeyBuf = pj2Str(prm.privkey_buf); + this->certLookupType= prm.cert_lookup.type; + this->certLookupKeyword = pj2Str(prm.cert_lookup.keyword); this->method = (pjsip_ssl_method)prm.method; this->proto = prm.proto; // The following will only work if sizeof(enum)==sizeof(int) @@ -256,6 +260,8 @@ void TlsConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error) NODE_READ_NUM_T ( this_node, pj_qos_type, qosType); readQosParams ( this_node, qosParams); NODE_READ_BOOL ( this_node, qosIgnoreError); + NODE_READ_NUM_T ( this_node, pj_ssl_cert_lookup_type, certLookupType); + NODE_READ_STRING ( this_node, certLookupKeyword); } void TlsConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error) @@ -278,6 +284,8 @@ void TlsConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error) NODE_WRITE_NUM_T ( this_node, pj_qos_type, qosType); writeQosParams ( this_node, qosParams); NODE_WRITE_BOOL ( this_node, qosIgnoreError); + NODE_WRITE_NUM_T ( this_node, pj_ssl_cert_lookup_type, certLookupType); + NODE_WRITE_STRING ( this_node, certLookupKeyword); } /////////////////////////////////////////////////////////////////////////////// From 7394b1efbd0dbd071ca3d05fc13cdb5b6677e965 Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Mon, 20 May 2024 08:50:05 +0200 Subject: [PATCH 023/491] pjsip_find_msg: Log warning if Content-Length field not found (#3960) --- pjsip/src/pjsip/sip_parser.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c index 24966b6586..54ba6dbba1 100644 --- a/pjsip/src/pjsip/sip_parser.c +++ b/pjsip/src/pjsip/sip_parser.c @@ -937,6 +937,8 @@ PJ_DEF(pj_status_t) pjsip_find_msg( const char *buf, pj_size_t size, /* Found Content-Length? */ if (content_length == -1) { + /* As per RFC3261 7.4.2, 7.5, 20.14, Content-Length is mandatory over TCP. */ + PJ_LOG(2, (THIS_FILE, "Field Content-Length not found!")); return status; } From a250942153daefbaab7a4b4dac8deb3cc5c5a176 Mon Sep 17 00:00:00 2001 From: Riza Sulistyo Date: Mon, 20 May 2024 13:50:21 +0700 Subject: [PATCH 024/491] Fix audiodev index (#3962) --- pjmedia/src/pjmedia/audiodev.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pjmedia/src/pjmedia/audiodev.c b/pjmedia/src/pjmedia/audiodev.c index 13f61b4fd8..eea7dfe0d5 100644 --- a/pjmedia/src/pjmedia/audiodev.c +++ b/pjmedia/src/pjmedia/audiodev.c @@ -427,11 +427,13 @@ PJ_DEF(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id, if (status != PJ_SUCCESS) return status; + status = f->op->get_dev_info(f, index, info); + /* Make sure device ID is the real ID (not PJMEDIA_AUD_DEFAULT_*_DEV) */ info->id = index; make_global_index(f->sys.drv_idx, &info->id); - return f->op->get_dev_info(f, index, info); + return status; } /* API: find device */ From 2e8f361f7f9914ecfe598f9bb05682a25e614313 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Mon, 27 May 2024 10:19:38 +0700 Subject: [PATCH 025/491] Fix assertion on call hangup from DTMF callback (#3970) --- pjmedia/src/pjmedia/stream.c | 29 ++++++++++++++++++++++++++-- pjmedia/src/pjmedia/transport_udp.c | 30 +++++++++++++++++++++++------ 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index 2e3200d6d2..18cae24436 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -110,6 +110,7 @@ struct dtmf struct pjmedia_stream { pjmedia_endpt *endpt; /**< Media endpoint. */ + pj_grp_lock_t *grp_lock; /**< Group lock. */ pjmedia_codec_mgr *codec_mgr; /**< Codec manager instance. */ pjmedia_stream_info si; /**< Creation parameter. */ pjmedia_port port; /**< Port interface. */ @@ -299,6 +300,7 @@ static pj_status_t send_rtcp(pjmedia_stream *stream, pj_bool_t with_xr, pj_bool_t with_fb); +static void stream_on_destroy(void *arg); #if TRACE_JB @@ -2453,7 +2455,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, PJ_ASSERT_RETURN(endpt && info && p_stream, PJ_EINVAL); - if (pool == NULL) { + if (1 || pool == NULL) { own_pool = pjmedia_endpt_create_pool( endpt, "strm%p", PJMEDIA_STREAM_SIZE, PJMEDIA_STREAM_INC); @@ -2897,6 +2899,14 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, att_param.rtp_cb2 = &on_rx_rtp; att_param.rtcp_cb = &on_rx_rtcp; + /* Attach handler to group lock from transport */ + if (tp->grp_lock) { + stream->grp_lock = tp->grp_lock; + pj_grp_lock_add_ref(stream->grp_lock); + pj_grp_lock_add_handler(stream->grp_lock, pool, stream, + &stream_on_destroy); + } + /* Only attach transport when stream is ready. */ stream->transport = tp; status = pjmedia_transport_attach2(tp, &att_param); @@ -3060,6 +3070,15 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, } +static void stream_on_destroy(void *arg) +{ + pjmedia_stream* stream = (pjmedia_stream*)arg; + + PJ_LOG(4,(stream->port.info.name.ptr, "Stream destroyed")); + pj_pool_safe_release(&stream->own_pool); +} + + /* * Destroy stream. */ @@ -3069,6 +3088,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); + PJ_LOG(4,(stream->port.info.name.ptr, "Stream destroying")); + /* Send RTCP BYE (also SDES & XR) */ if (stream->transport && !stream->rtcp_sdes_bye_disabled) { #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) @@ -3167,7 +3188,11 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) } #endif - pj_pool_safe_release(&stream->own_pool); + if (stream->grp_lock) { + pj_grp_lock_dec_ref(stream->grp_lock); + } else { + stream_on_destroy(stream); + } return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/transport_udp.c b/pjmedia/src/pjmedia/transport_udp.c index cf3471ced9..05723029c2 100644 --- a/pjmedia/src/pjmedia/transport_udp.c +++ b/pjmedia/src/pjmedia/transport_udp.c @@ -115,6 +115,7 @@ static void on_rtp_data_sent(pj_ioqueue_key_t *key, static void on_rx_rtcp(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read); +static void transport_on_destroy(void *arg); /* * These are media transport operations. @@ -355,6 +356,7 @@ PJ_DEF(pj_status_t) pjmedia_transport_udp_attach( pjmedia_endpt *endpt, goto on_error; pj_grp_lock_add_ref(grp_lock); + pj_grp_lock_add_handler(grp_lock, pool, tp, &transport_on_destroy); tp->base.grp_lock = grp_lock; /* Setup RTP socket with the ioqueue */ @@ -436,6 +438,15 @@ PJ_DEF(pj_status_t) pjmedia_transport_udp_attach( pjmedia_endpt *endpt, } +static void transport_on_destroy(void *arg) +{ + struct transport_udp *udp = (struct transport_udp*) arg; + + PJ_LOG(4, (udp->base.name, "UDP media transport destroyed")); + pj_pool_safe_release(&udp->pool); +} + + /** * Close UDP transport. */ @@ -449,6 +460,8 @@ static pj_status_t transport_destroy(pjmedia_transport *tp) /* Must not close while application is using this */ //PJ_ASSERT_RETURN(!udp->attached, PJ_EINVALIDOP); + PJ_LOG(4,(udp->base.name, "UDP media transport destroying")); + /* The following calls to pj_ioqueue_unregister() will block the execution * if callback is still being called because allow_concurrent is false. * So it is safe to release the pool immediately after. @@ -474,9 +487,6 @@ static pj_status_t transport_destroy(pjmedia_transport *tp) pj_grp_lock_dec_ref(tp->grp_lock); - PJ_LOG(4,(udp->base.name, "UDP media transport destroyed")); - pj_pool_release(udp->pool); - return PJ_SUCCESS; } @@ -574,6 +584,10 @@ static void on_rx_rtp(pj_ioqueue_key_t *key, call_rtp_cb(udp, bytes_read, &rem_switch); } + /* Transport may be destroyed from the callback! */ + if (!udp->rtp_key || !udp->started) + break; + #if defined(PJMEDIA_TRANSPORT_SWITCH_REMOTE_ADDR) && \ (PJMEDIA_TRANSPORT_SWITCH_REMOTE_ADDR == 1) if (rem_switch && @@ -617,9 +631,9 @@ static void on_rx_rtp(pj_ioqueue_key_t *key, bytes_read = sizeof(udp->rtp_pkt); udp->rtp_addrlen = sizeof(udp->rtp_src_addr); status = pj_ioqueue_recvfrom(udp->rtp_key, &udp->rtp_read_op, - udp->rtp_pkt, &bytes_read, 0, - &udp->rtp_src_addr, - &udp->rtp_addrlen); + udp->rtp_pkt, &bytes_read, 0, + &udp->rtp_src_addr, + &udp->rtp_addrlen); if (status != PJ_EPENDING && status != PJ_SUCCESS) { if (transport_restarted && last_err == status) { @@ -709,6 +723,10 @@ static void on_rx_rtcp(pj_ioqueue_key_t *key, do { call_rtcp_cb(udp, bytes_read); + /* Transport may be destroyed from the callback! */ + if (!udp->rtcp_key || !udp->started) + break; + #if defined(PJMEDIA_TRANSPORT_SWITCH_REMOTE_ADDR) && \ (PJMEDIA_TRANSPORT_SWITCH_REMOTE_ADDR == 1) /* Check if RTCP source address is the same as the configured From 13741d9eff3fce4f1c88715881fa85f454f33737 Mon Sep 17 00:00:00 2001 From: Andreas Peldszus Date: Mon, 27 May 2024 05:23:02 +0200 Subject: [PATCH 026/491] Fix yaml error in github feature template (#3972) --- .github/ISSUE_TEMPLATE/feature_request.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index a10f536dea..21c4af8062 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -19,8 +19,7 @@ body: id: alt-solution attributes: label: Describe alternatives you've considered - description: A clear and concise description of any alternative solutions or features you've -considered. + description: A clear and concise description of any alternative solutions or features you've considered. - type: textarea id: context attributes: From c715e2e878bf8cf829ee27f03f35fdbf62d08fe6 Mon Sep 17 00:00:00 2001 From: sauwming Date: Thu, 30 May 2024 06:59:55 +0800 Subject: [PATCH 027/491] Fix version string in Python setup (#3976) --- pjsip-apps/src/swig/python/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pjsip-apps/src/swig/python/setup.py b/pjsip-apps/src/swig/python/setup.py index 4259f39f6b..c49cd64e4e 100644 --- a/pjsip-apps/src/swig/python/setup.py +++ b/pjsip-apps/src/swig/python/setup.py @@ -63,7 +63,7 @@ if pj_version_rev: pj_version += "." + pj_version_rev if pj_version_suffix: - pj_version += "-" + pj_version_suffix + pj_version += pj_version_suffix #print 'PJ_VERSION = "'+ pj_version + '"' From 2d5e35182afcb03979451d56ca6e46695975d4e3 Mon Sep 17 00:00:00 2001 From: sauwming Date: Thu, 30 May 2024 07:00:05 +0800 Subject: [PATCH 028/491] Prevent pjmedia_codec_param.info.enc_ptime_denum division by zero in stream (#3975) --- pjmedia/src/pjmedia/stream.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index 18cae24436..b09a42ec33 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -2573,6 +2573,9 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, if (stream->codec_param.info.frm_ptime_denum < 1) stream->codec_param.info.frm_ptime_denum = 1; + if (stream->codec_param.info.enc_ptime_denum < 1) + stream->codec_param.info.enc_ptime_denum = 1; + /* Init the codec. */ status = pjmedia_codec_init(stream->codec, pool); if (status != PJ_SUCCESS) From 28c54a42f6978e6c5b8d051bb9ae984a3a6130d7 Mon Sep 17 00:00:00 2001 From: Riza Sulistyo Date: Thu, 13 Jun 2024 10:05:48 +0700 Subject: [PATCH 029/491] Lyra support (#3949) * Initial implementation * Fix --with-lyra configure option * Modification based on comments * Modification based on comments - Add bitrate config on SDP, allowing different bitrate on local and remote - Fix some compile warning - Set [--with-lyra]/model_coeffs as default to PJMEDIA_CODEC_LYRA_DEFAULT_MODEL_PATH * Modification based on comments: - Update some doc - Change field setting name - Remove un-needed code - Suppress compile warning * Modification based on comments - Add comments - Add compile time setting to enable certain samplerate - Get decoder bitrate from SDP - More lenient on codec parse * Add build config for Visual Studio --- aconfigure | 118 +++ aconfigure.ac | 78 ++ pjmedia/build/os-auto.mak.in | 8 + pjmedia/build/pjmedia_codec.vcxproj | 8 +- pjmedia/build/pjmedia_codec.vcxproj.filters | 6 + pjmedia/include/pjmedia-codec.h | 1 + pjmedia/include/pjmedia-codec/config.h | 69 ++ .../include/pjmedia-codec/config_auto.h.in | 5 + pjmedia/include/pjmedia-codec/lyra.h | 119 +++ pjmedia/include/pjmedia-codec/types.h | 4 + pjmedia/src/pjmedia-codec/audio_codecs.c | 7 + pjmedia/src/pjmedia-codec/lyra.cpp | 726 ++++++++++++++++++ pjsip/include/pjsua2/endpoint.hpp | 15 + pjsip/include/pjsua2/media.hpp | 30 + pjsip/src/pjsua2/endpoint.cpp | 30 + pjsip/src/pjsua2/media.cpp | 28 +- 16 files changed, 1245 insertions(+), 7 deletions(-) create mode 100755 pjmedia/include/pjmedia-codec/lyra.h create mode 100755 pjmedia/src/pjmedia-codec/lyra.cpp diff --git a/aconfigure b/aconfigure index 0ef178b110..77346d94da 100755 --- a/aconfigure +++ b/aconfigure @@ -658,6 +658,8 @@ ac_webrtc_instset ac_no_webrtc ac_no_yuv ac_no_srtp +ac_lyra_model_path +ac_no_lyra_codec ac_no_bcg729 opus_present opus_h_present @@ -885,6 +887,8 @@ with_opus enable_opus with_bcg729 enable_bcg729 +with_lyra +enable_lyra enable_libsrtp enable_libyuv enable_libwebrtc @@ -1574,6 +1578,7 @@ Optional Features: autodetect) --disable-bcg729 Disable bcg729 (default: not disabled) + --disable-lyra Disable lyra (default: not disabled) --disable-libsrtp Exclude libsrtp in the build --disable-libyuv Exclude libyuv in the build --disable-libwebrtc Exclude libwebrtc in the build @@ -1641,6 +1646,7 @@ Optional Packages: --with-silk=DIR Specify alternate SILK prefix --with-opus=DIR Specify alternate OPUS prefix --with-bcg729=DIR Specify alternate bcg729 prefix + --with-lyra=DIR Specify alternate lyra prefix Some influential environment variables: CC C compiler command @@ -10118,6 +10124,118 @@ fi +# Check whether --with-lyra was given. +if test ${with_lyra+y} +then : + withval=$with_lyra; +else $as_nop + with_lyra=no + +fi + + +if test "x$ac_cross_compile" != "x" -a "x$with_lyra" = "xno"; then + enable_lyra=no +fi + + + +# Check whether --enable-lyra was given. +if test ${enable_lyra+y} +then : + enableval=$enable_lyra; + if test "$enable_lyra" = "no"; then + ac_no_lyra_codec=1 + printf "%s\n" "#define PJMEDIA_HAS_LYRA_CODEC 0" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if lyra is disabled... yes" >&5 +printf "%s\n" "Checking if lyra is disabled... yes" >&6; } + fi + +else $as_nop + + if test "x$with_lyra" != "xno" -a "x$with_lyra" != "x"; then + LYRA_PREFIX=$with_lyra + LYRA_CPPFLAGS="-DGLOG_DEPRECATED=__attribute__((deprecated)) -DGLOG_EXPORT=__attribute__((visibility(\"default\"))) -DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))" + LYRA_CXXFLAGS="-std=c++17 -Wno-deprecated-builtins -I$LYRA_PREFIX/include/com_google_absl -I$LYRA_PREFIX/include/gulrak_filesystem -I$LYRA_PREFIX/include/com_google_glog/src -I$LYRA_PREFIX" + LYRA_LDFLAGS="-L$LYRA_PREFIX/lib" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using lyra prefix... $with_lyra" >&5 +printf "%s\n" "Using lyra prefix... $with_lyra" >&6; } + else + LYRA_CXXFLAGS="" + LYRA_LDFLAGS="" + fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking lyra usability" >&5 +printf %s "checking lyra usability... " >&6; } + + LYRA_LIBS="-llyra" + + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CXXFLAGS="$CXXFLAGS" + SAVED_CPPFLAGS="$CPPFLAGS" + + LIBS="$LYRA_LIBS $LIBS" + LDFLAGS="$LYRA_LDFLAGS $LDFLAGS" + CXXFLAGS="$LYRA_CXXFLAGS $CXXFLAGS" + CPPFLAGS="$LYRA_CPPFLAGS $CPPFLAGS" + + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include "lyra_decoder.h" + +int +main (void) +{ +std::unique_ptr dec = chromemedia::codec::LyraDecoder::Create(8000,1,""); + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO" +then : + + ac_lyra_model_path="$LYRA_PREFIX/model_coeffs" + printf "%s\n" "#define PJMEDIA_HAS_LYRA_CODEC 1" >>confdefs.h + + LYRA_CPPFLAGS="'-DGLOG_DEPRECATED=__attribute__((deprecated))' '-DGLOG_EXPORT=__attribute__((visibility(\"default\")))' '-DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))'" + CXXFLAGS="$LYRA_CPPFLAGS $CXXFLAGS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } + +else $as_nop + + ac_no_lyra_codec=1 + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CXXFLAGS="$SAVED_CXXFLAGS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + CPPFLAGS="$SAVED_CPPFLAGS" + +fi + + + + # Check whether --enable-libsrtp was given. if test ${enable_libsrtp+y} diff --git a/aconfigure.ac b/aconfigure.ac index e49148fd79..cea9d52090 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -2311,6 +2311,84 @@ AC_ARG_ENABLE(bcg729, ]) +dnl # lyra prefix +AC_ARG_WITH(lyra, + AS_HELP_STRING([--with-lyra=DIR], + [Specify alternate lyra prefix]), + [], + [with_lyra=no] + ) + +dnl # Do not use default lyra installation if we are cross-compiling +if test "x$ac_cross_compile" != "x" -a "x$with_lyra" = "xno"; then + enable_lyra=no +fi + +dnl # lyra +AC_SUBST(ac_no_lyra_codec) +AC_SUBST(ac_lyra_model_path) +AC_ARG_ENABLE(lyra, + AS_HELP_STRING([--disable-lyra], + [Disable lyra (default: not disabled)]), + [ + if test "$enable_lyra" = "no"; then + [ac_no_lyra_codec=1] + AC_DEFINE(PJMEDIA_HAS_LYRA_CODEC,0) + AC_MSG_RESULT([Checking if lyra is disabled... yes]) + fi + ], + [ + if test "x$with_lyra" != "xno" -a "x$with_lyra" != "x"; then + LYRA_PREFIX=$with_lyra + dnl # inside autoconf, single quoted preprocessor definition will raise error + LYRA_CPPFLAGS="-DGLOG_DEPRECATED=__attribute__((deprecated)) -DGLOG_EXPORT=__attribute__((visibility(\"default\"))) -DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))" + LYRA_CXXFLAGS="-std=c++17 -Wno-deprecated-builtins -I$LYRA_PREFIX/include/com_google_absl -I$LYRA_PREFIX/include/gulrak_filesystem -I$LYRA_PREFIX/include/com_google_glog/src -I$LYRA_PREFIX" + LYRA_LDFLAGS="-L$LYRA_PREFIX/lib" + AC_MSG_RESULT([Using lyra prefix... $with_lyra]) + else + LYRA_CXXFLAGS="" + LYRA_LDFLAGS="" + fi + + AC_MSG_CHECKING([lyra usability]) + + LYRA_LIBS="-llyra" + + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CXXFLAGS="$CXXFLAGS" + SAVED_CPPFLAGS="$CPPFLAGS" + + LIBS="$LYRA_LIBS $LIBS" + LDFLAGS="$LYRA_LDFLAGS $LDFLAGS" + CXXFLAGS="$LYRA_CXXFLAGS $CXXFLAGS" + CPPFLAGS="$LYRA_CPPFLAGS $CPPFLAGS" + + AC_LANG_PUSH([C++]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include "lyra_decoder.h" + ]], + [std::unique_ptr dec = chromemedia::codec::LyraDecoder::Create(8000,1,"");] + )], + [ + [ac_lyra_model_path="$LYRA_PREFIX/model_coeffs"] + AC_DEFINE(PJMEDIA_HAS_LYRA_CODEC,1) + dnl # use single quoted preprocessor definition + LYRA_CPPFLAGS="'-DGLOG_DEPRECATED=__attribute__((deprecated))' '-DGLOG_EXPORT=__attribute__((visibility(\"default\")))' '-DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))'" + CXXFLAGS="$LYRA_CPPFLAGS $CXXFLAGS" + AC_MSG_RESULT(yes) + ], + [ + [ac_no_lyra_codec=1] + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CXXFLAGS="$SAVED_CXXFLAGS" + AC_MSG_RESULT(no) + ]) + AC_LANG_POP([C++]) + CPPFLAGS="$SAVED_CPPFLAGS" + ]) + + dnl # Include libsrtp AC_SUBST(ac_no_srtp) diff --git a/pjmedia/build/os-auto.mak.in b/pjmedia/build/os-auto.mak.in index 20a5b6f023..d815a330ba 100644 --- a/pjmedia/build/os-auto.mak.in +++ b/pjmedia/build/os-auto.mak.in @@ -74,6 +74,7 @@ AC_NO_OPENCORE_AMRNB=@ac_no_opencore_amrnb@ AC_NO_OPENCORE_AMRWB=@ac_no_opencore_amrwb@ AC_NO_BCG729=@ac_no_bcg729@ AC_NO_ANDROID_MEDIACODEC=@ac_no_mediacodec@ +AC_NO_LYRA_CODEC=@ac_no_lyra_codec@ export CODEC_OBJS= @@ -152,6 +153,13 @@ else export CODEC_OBJS += and_aud_mediacodec.o and_vid_mediacodec.o endif +ifeq ($(AC_NO_LYRA_CODEC),1) +export CFLAGS += -DPJMEDIA_HAS_LYRA_CODEC=0 +else +export CODEC_OBJS += lyra.o +export CFLAGS += -DPJMEDIA_CODEC_LYRA_DEFAULT_MODEL_PATH='"@ac_lyra_model_path@"' +endif + # # SRTP # diff --git a/pjmedia/build/pjmedia_codec.vcxproj b/pjmedia/build/pjmedia_codec.vcxproj index 1d4cac128a..c11a4422b0 100644 --- a/pjmedia/build/pjmedia_codec.vcxproj +++ b/pjmedia/build/pjmedia_codec.vcxproj @@ -467,7 +467,6 @@ ../include;../../pjlib/include;../../third_party/speex/include;../../third_party;%(AdditionalIncludeDirectories) _LIB;%(PreprocessorDefinitions) - ..\lib\pjmedia-codec-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration).lib @@ -689,6 +688,12 @@ + + GLOG_DEPRECATED=__declspec(deprecated);GLOG_EXPORT=;GLOG_NO_ABBREVIATED_SEVERITIES;GLOG_NO_EXPORT=; +EIGEN_MPL2_ONLY;EIGEN_MAX_ALIGN_BYTES=64;XNN_ENABLE_ASSEMBLY=1;XNN_ENABLE_JIT=0;XNN_ENABLE_SPARSE=1;XNN_LOG_LEVEL=2;PTHREADPOOL_NO_DEPRECATED_API;XNN_ENABLE_MEMOPT=1;XNN_WASMSIMD_VERSION=87;EIGEN_ALTIVEC_USE_CUSTOM_PACK=0;TFLITE_BUILD_WITH_XNNPACK_DELEGATE;_USE_MATH_DEFINES;__DATE__="redacted";__TIMESTAMP__="redacted";__TIME__="redacted";NOGDI;__PRETTY_FUNCTION__=__FUNCSIG__;_HAS_DEPRECATED_RESULT_OF=1;%(PreprocessorDefinitions) + stdcpp17 + 4117;4244;4267%(DisableSpecificWarnings) + @@ -715,6 +720,7 @@ + diff --git a/pjmedia/build/pjmedia_codec.vcxproj.filters b/pjmedia/build/pjmedia_codec.vcxproj.filters index 2f888d6a54..484f4d9768 100644 --- a/pjmedia/build/pjmedia_codec.vcxproj.filters +++ b/pjmedia/build/pjmedia_codec.vcxproj.filters @@ -83,6 +83,9 @@ Source Files + + Source Files + @@ -163,5 +166,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/pjmedia/include/pjmedia-codec.h b/pjmedia/include/pjmedia-codec.h index 6609458087..49ef723931 100644 --- a/pjmedia/include/pjmedia-codec.h +++ b/pjmedia/include/pjmedia-codec.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/pjmedia/include/pjmedia-codec/config.h b/pjmedia/include/pjmedia-codec/config.h index 048ba2935b..5d7a15b43f 100644 --- a/pjmedia/include/pjmedia-codec/config.h +++ b/pjmedia/include/pjmedia-codec/config.h @@ -540,6 +540,75 @@ #endif + /** + * Enable Lyra codec. + * + * Default: 0 + */ +#ifndef PJMEDIA_HAS_LYRA_CODEC +# define PJMEDIA_HAS_LYRA_CODEC 0 +#endif + +/** + * Lyra default bitrate setting + * + * Default: 3200 (available bitrate:3200, 6000, 9200) + */ +#ifndef PJMEDIA_CODEC_LYRA_DEFAULT_BIT_RATE +# define PJMEDIA_CODEC_LYRA_DEFAULT_BIT_RATE 3200 +#endif + +/** + * Lyra default model path containing lyra_config.binarypb, lyragan.tflite, + * quantizer.tflite and soundstream_encoder.tflite file. If autoconf is used, + * it will be set to "[lyra src folder]/model_coeffs". + * + * Default: "model_coeffs" + */ +#ifndef PJMEDIA_CODEC_LYRA_DEFAULT_MODEL_PATH +# define PJMEDIA_CODEC_LYRA_DEFAULT_MODEL_PATH "model_coeffs" +#endif + +/** + * Settings to enable Lyra codec 8KHz. This option is only used + * when PJMEDIA_HAS_LYRA_CODEC is enabled. + * + * Default: 0 + */ +#ifndef PJMEDIA_CODEC_LYRA_HAS_8KHZ +# define PJMEDIA_CODEC_LYRA_HAS_8KHZ 0 +#endif + +/** + * Settings to enable Lyra codec 16KHz. This option is only used + * when PJMEDIA_HAS_LYRA_CODEC is enabled. + * + * Default: 1 + */ +#ifndef PJMEDIA_CODEC_LYRA_HAS_16KHZ +# define PJMEDIA_CODEC_LYRA_HAS_16KHZ 1 +#endif + +/** + * Settings to enable Lyra codec 32KHz. This option is only used + * when PJMEDIA_HAS_LYRA_CODEC is enabled. + * + * Default: 0 + */ +#ifndef PJMEDIA_CODEC_LYRA_HAS_32KHZ +# define PJMEDIA_CODEC_LYRA_HAS_32KHZ 0 +#endif + +/** + * Settings to enable Lyra codec 48KHz. This option is only used + * when PJMEDIA_HAS_LYRA_CODEC is enabled. + * + * Default: 0 + */ +#ifndef PJMEDIA_CODEC_LYRA_HAS_48KHZ +# define PJMEDIA_CODEC_LYRA_HAS_48KHZ 0 +#endif + /** * Specify if FFMPEG codecs are available. * diff --git a/pjmedia/include/pjmedia-codec/config_auto.h.in b/pjmedia/include/pjmedia-codec/config_auto.h.in index 3cc91263ef..2a2301e914 100644 --- a/pjmedia/include/pjmedia-codec/config_auto.h.in +++ b/pjmedia/include/pjmedia-codec/config_auto.h.in @@ -98,6 +98,11 @@ #undef PJMEDIA_HAS_ANDROID_MEDIACODEC #endif +/* Lyra codec */ +#ifndef PJMEDIA_HAS_LYRA_CODEC +#undef PJMEDIA_HAS_LYRA_CODEC +#endif + #endif /* __PJMEDIA_CODEC_CONFIG_AUTO_H_ */ diff --git a/pjmedia/include/pjmedia-codec/lyra.h b/pjmedia/include/pjmedia-codec/lyra.h new file mode 100755 index 0000000000..70e6941215 --- /dev/null +++ b/pjmedia/include/pjmedia-codec/lyra.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2024 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJMEDIA_CODEC_LYRA_H__ +#define __PJMEDIA_CODEC_LYRA_H__ + + /** + * @file pjmedia-codec/lyra.hpp + * @brief lyra codec. + */ + +#include + + /** + * @defgroup PJMED_LYRA lyra Codec + * @ingroup PJMEDIA_CODEC_CODECS + * @brief Implementation of lyra Codec + * @{ + * + * This section describes functions to initialize and register lyra codec + * factory to the codec manager. After the codec factory has been registered, + * application can use @ref PJMEDIA_CODEC API to manipulate the codec. + * + * Lyra codec supports 16-bit PCM audio signal with sampling rate of (8000Hz, + * 16000Hz, 32000Hz and 48000Hz), frame length 20ms, and resulting in + * bitrate 3200bps, 6000bps and 9200bps. + */ + +PJ_BEGIN_DECL + +/** + * Lyra codec setting; + */ +typedef struct pjmedia_codec_lyra_config +{ + /** + * The value represents the decoder bitrate requested by the receiver. + * Endpoints can be configured with different bitrates. For example, + * the local endpoint might be set to a bitrate of 3200, while + * the remote endpoint is set to 6000. In this scenario, the remote + * endpoint will send data at 3200 bitrate, while the local endpoint + * will send data at 6000 bitrate. Valid bitrate: 3200, 6000, 9200. + * By default it is set to PJMEDIA_CODEC_LYRA_DEFAULT_BIT_RATE. + */ + unsigned bit_rate; + + /** + * Lyra required some additional (model) files, including + * \b lyra_config.binarypb , \b lyragan.tflite , \b quantizer.tflite and + * \b soundstream_encoder.tflite . + * This setting represents the folder containing the above files. + * The specified folder should contain these files. If an invalid folder + * is provided, the codec creation will fail. + */ + pj_str_t model_path; + +} pjmedia_codec_lyra_config; + +/** + * Initialize and register lyra codec factory to pjmedia endpoint. + * + * @param endpt The pjmedia endpoint. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_codec_lyra_init(pjmedia_endpt *endpt); + +/** + * Unregister lyra codec factory from pjmedia endpoint and deinitialize + * the lyra codec library. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_codec_lyra_deinit(void); + +/** + * Get the default Lyra configuration. + * + * @param cfg Lyra codec configuration. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_codec_lyra_get_config( pjmedia_codec_lyra_config *cfg); + +/** + * Set the default Lyra configuration. + * + * @param cfg Lyra codec configuration. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_codec_lyra_set_config(const pjmedia_codec_lyra_config *cfg); + + +PJ_END_DECL + + +/** + * @} + */ + +#endif /* __PJMEDIA_CODEC_LYRA_H__ */ + diff --git a/pjmedia/include/pjmedia-codec/types.h b/pjmedia/include/pjmedia-codec/types.h index c3bad71e94..256aea9c55 100644 --- a/pjmedia/include/pjmedia-codec/types.h +++ b/pjmedia/include/pjmedia-codec/types.h @@ -83,6 +83,10 @@ enum pjmedia_audio_pt PJMEDIA_RTP_PT_G7221_RSV1, /**< G722.1 reserve */ PJMEDIA_RTP_PT_G7221_RSV2, /**< G722.1 reserve */ PJMEDIA_RTP_PT_OPUS, /**< OPUS */ + PJMEDIA_RTP_PT_LYRA_8, /**< LYRA @ 8KHz */ + PJMEDIA_RTP_PT_LYRA_16, /**< LYRA @ 16KHz */ + PJMEDIA_RTP_PT_LYRA_32, /**< LYRA @ 32KHz */ + PJMEDIA_RTP_PT_LYRA_48, /**< LYRA @ 48KHz */ #if PJMEDIA_CODEC_L16_HAS_8KHZ_MONO PJMEDIA_RTP_PT_L16_8KHZ_MONO, /**< L16 @ 8KHz, mono */ #endif diff --git a/pjmedia/src/pjmedia-codec/audio_codecs.c b/pjmedia/src/pjmedia-codec/audio_codecs.c index 0405d2994a..d928520f6d 100644 --- a/pjmedia/src/pjmedia-codec/audio_codecs.c +++ b/pjmedia/src/pjmedia-codec/audio_codecs.c @@ -142,6 +142,13 @@ pjmedia_codec_register_audio_codecs(pjmedia_endpt *endpt, return status; #endif +#if PJMEDIA_HAS_LYRA_CODEC + /* Register Lyra */ + status = pjmedia_codec_lyra_init(endpt); + if (status != PJ_SUCCESS) + return status; +#endif + return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia-codec/lyra.cpp b/pjmedia/src/pjmedia-codec/lyra.cpp new file mode 100755 index 0000000000..33fefd152d --- /dev/null +++ b/pjmedia/src/pjmedia-codec/lyra.cpp @@ -0,0 +1,726 @@ +/* + * Copyright (C)2024 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include + +#if defined(PJMEDIA_HAS_LYRA_CODEC) && PJMEDIA_HAS_LYRA_CODEC != 0 + +#include "lyra_encoder.h" +#include "lyra_decoder.h" + +#define THIS_FILE "lyra.cpp" + +// Available bitrate : 3200, 6000, 9200 +#define LYRA_MAX_BITRATE 9200 +#define LYRA_DEFAULT_VAD 1 +#define LYRA_MAX_PATH_LEN 64 +#define LYRA_DEFAULT_PATH "model_coeffs" + +static char LYRA_STR[] = "lyra"; +static char BIT_RATE_STR[] = "bitrate"; + +using namespace chromemedia::codec; + +/* Prototypes for lyra factory */ +static pj_status_t lyra_test_alloc(pjmedia_codec_factory *factory, + const pjmedia_codec_info *id); +static pj_status_t lyra_default_attr(pjmedia_codec_factory *factory, + const pjmedia_codec_info* id, + pjmedia_codec_param *attr); +static pj_status_t lyra_enum_codecs(pjmedia_codec_factory *factory, + unsigned *count, + pjmedia_codec_info codecs[]); +static pj_status_t lyra_alloc_codec(pjmedia_codec_factory *factory, + const pjmedia_codec_info *id, + pjmedia_codec **p_codec); +static pj_status_t lyra_dealloc_codec(pjmedia_codec_factory *factory, + pjmedia_codec *codec); + +/* Prototypes for lyra implementation. */ +static pj_status_t lyra_codec_init(pjmedia_codec *codec, + pj_pool_t *pool); +static pj_status_t lyra_codec_open(pjmedia_codec *codec, + pjmedia_codec_param *attr); +static pj_status_t lyra_codec_close(pjmedia_codec *codec); +static pj_status_t lyra_codec_modify(pjmedia_codec *codec, + const pjmedia_codec_param *attr); +static pj_status_t lyra_codec_parse(pjmedia_codec *codec, + void *pkt, + pj_size_t pkt_size, + const pj_timestamp *ts, + unsigned *frame_cnt, + pjmedia_frame frames[]); +static pj_status_t lyra_codec_encode(pjmedia_codec *codec, + const struct pjmedia_frame *input, + unsigned output_buf_len, + struct pjmedia_frame *output); +static pj_status_t lyra_codec_decode(pjmedia_codec *codec, + const struct pjmedia_frame *input, + unsigned output_buf_len, + struct pjmedia_frame *output); +static pj_status_t lyra_codec_recover(pjmedia_codec *codec, + unsigned output_buf_len, + struct pjmedia_frame *output); + +/* Definition for lyra codec operations. */ +static pjmedia_codec_op lyra_op = +{ + &lyra_codec_init, + &lyra_codec_open, + &lyra_codec_close, + &lyra_codec_modify, + &lyra_codec_parse, + &lyra_codec_encode, + &lyra_codec_decode, + &lyra_codec_recover +}; + +/* Definition for lyra codec factory operations. */ +static pjmedia_codec_factory_op lyra_factory_op = +{ + &lyra_test_alloc, + &lyra_default_attr, + &lyra_enum_codecs, + &lyra_alloc_codec, + &lyra_dealloc_codec, + &pjmedia_codec_lyra_deinit +}; + +struct lyra_param +{ + int enabled; /* Is this mode enabled? */ + int pt; /* Payload type. */ + unsigned clock_rate; /* Default sampling rate to be used.*/ +}; + +/* Lyra factory */ +struct lyra_factory +{ + pjmedia_codec_factory base; + pjmedia_endpt *endpt; + pj_pool_t *pool; + char model_path_buf[LYRA_MAX_PATH_LEN]; + struct lyra_param param[4]; +}; + +/* lyra codec private data. */ +struct lyra_data +{ + pj_pool_t *pool; + pj_mutex_t *mutex; + pj_bool_t vad_enabled; + pj_bool_t plc_enabled; + unsigned samples_per_frame; + unsigned enc_bit_rate; + unsigned dec_bit_rate; + + std::unique_ptr enc; + std::unique_ptr dec; +}; + +/* Codec factory instance */ +static struct lyra_factory lyra_factory; + +static pjmedia_codec_lyra_config lyra_cfg; + +/* + * Initialize and register lyra codec factory to pjmedia endpoint. + */ +PJ_DEF(pj_status_t) pjmedia_codec_lyra_init(pjmedia_endpt *endpt) +{ + pj_status_t status; + pjmedia_codec_mgr *codec_mgr; + + PJ_ASSERT_RETURN(endpt, PJ_EINVAL); + + if (lyra_factory.pool != NULL) + return PJ_SUCCESS; + + /* Create the Lyra codec factory */ + lyra_factory.base.op = &lyra_factory_op; + lyra_factory.base.factory_data = &lyra_factory; + lyra_factory.endpt = endpt; + + lyra_factory.pool = pjmedia_endpt_create_pool(endpt, "lyra-factory", + 1024, 1024); + if (!lyra_factory.pool) { + PJ_LOG(2, (THIS_FILE, "Unable to create memory pool for Lyra codec")); + return PJ_ENOMEM; + } + lyra_factory.param[0].enabled = PJMEDIA_CODEC_LYRA_HAS_8KHZ; + lyra_factory.param[0].pt = PJMEDIA_RTP_PT_LYRA_8; + lyra_factory.param[0].clock_rate = 8000; + + lyra_factory.param[1].enabled = PJMEDIA_CODEC_LYRA_HAS_16KHZ; + lyra_factory.param[1].pt = PJMEDIA_RTP_PT_LYRA_16; + lyra_factory.param[1].clock_rate = 16000; + + lyra_factory.param[2].enabled = PJMEDIA_CODEC_LYRA_HAS_32KHZ; + lyra_factory.param[2].pt = PJMEDIA_RTP_PT_LYRA_32; + lyra_factory.param[2].clock_rate = 32000; + + lyra_factory.param[3].enabled = PJMEDIA_CODEC_LYRA_HAS_48KHZ; + lyra_factory.param[3].pt = PJMEDIA_RTP_PT_LYRA_48; + lyra_factory.param[3].clock_rate = 48000; + + /* Get the codec manager */ + codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); + if (!codec_mgr) { + PJ_LOG(2, (THIS_FILE, "Unable to get the codec manager")); + status = PJ_EINVALIDOP; + goto on_codec_factory_error; + } + + /* Register the codec factory */ + status = pjmedia_codec_mgr_register_factory(codec_mgr, + &lyra_factory.base); + if (status != PJ_SUCCESS) { + PJ_LOG(2, (THIS_FILE, "Unable to register the codec factory")); + goto on_codec_factory_error; + } + + lyra_cfg.bit_rate = PJMEDIA_CODEC_LYRA_DEFAULT_BIT_RATE; + pj_ansi_strxcpy(lyra_factory.model_path_buf, + PJMEDIA_CODEC_LYRA_DEFAULT_MODEL_PATH , + sizeof(lyra_factory.model_path_buf)); + lyra_cfg.model_path = pj_str(lyra_factory.model_path_buf); + + return PJ_SUCCESS; + +on_codec_factory_error: + pj_pool_release(lyra_factory.pool); + lyra_factory.pool = NULL; + return status; +} + +/* + * Unregister lyra codec factory from pjmedia endpoint and deinitialize + * the lyra codec library. + */ +PJ_DEF(pj_status_t) pjmedia_codec_lyra_deinit(void) +{ + pj_status_t status; + pjmedia_codec_mgr *codec_mgr; + + if (lyra_factory.pool == NULL) + return PJ_SUCCESS; + + /* Get the codec manager */ + codec_mgr = pjmedia_endpt_get_codec_mgr(lyra_factory.endpt); + if (!codec_mgr) { + PJ_LOG(2, (THIS_FILE, "Unable to get the codec manager")); + pj_pool_release(lyra_factory.pool); + lyra_factory.pool = NULL; + return PJ_EINVALIDOP; + } + + /* Unregister the codec factory */ + status = pjmedia_codec_mgr_unregister_factory(codec_mgr, + &lyra_factory.base); + if (status != PJ_SUCCESS) + PJ_LOG(2, (THIS_FILE, "Unable to unregister the codec factory")); + + /* Release the memory pool */ + pj_pool_release(lyra_factory.pool); + lyra_factory.pool = NULL; + lyra_factory.endpt = NULL; + + return status; +} + +/* Prototypes for lyra factory */ +static pj_status_t lyra_test_alloc(pjmedia_codec_factory *factory, + const pjmedia_codec_info *info) +{ + unsigned i; + + PJ_UNUSED_ARG(factory); + + /* Type MUST be audio. */ + if (info->type != PJMEDIA_TYPE_AUDIO) + return PJMEDIA_CODEC_EUNSUP; + + /* Check encoding name. */ + if (pj_strcmp2(&info->encoding_name, LYRA_STR) != 0) + return PJMEDIA_CODEC_EUNSUP; + + /* Check clock-rate */ + for (i = 0; i < PJ_ARRAY_SIZE(lyra_factory.param); ++i) { + if (info->clock_rate == lyra_factory.param[i].clock_rate) { + return PJ_SUCCESS; + } + } + + /* Unsupported, or mode is disabled. */ + return PJMEDIA_CODEC_EUNSUP; +} + +static char *get_bit_rate_val(unsigned bit_rate) +{ + static char buf_3200[] = "3200"; + static char buf_6000[] = "6000"; + static char buf_9200[] = "9200"; + + switch (bit_rate) { + case 3200: + return buf_3200; + case 6000: + return buf_6000; + case 9200: + return buf_9200; + } + return 0; +} + +static pj_status_t lyra_default_attr(pjmedia_codec_factory *factory, + const pjmedia_codec_info *id, + pjmedia_codec_param *attr) +{ + PJ_ASSERT_RETURN(factory == &lyra_factory.base, PJ_EINVAL); + + unsigned idx = 0; + pj_bzero(attr, sizeof(pjmedia_codec_param)); + attr->info.pt = (pj_uint8_t)id->pt; + attr->info.channel_cnt = 1; + + for (; idx < PJ_ARRAY_SIZE(lyra_factory.param); ++idx) { + if (lyra_factory.param[idx].enabled && + id->clock_rate == lyra_factory.param[idx].clock_rate) + { + break; + } + } + if (idx == PJ_ARRAY_SIZE(lyra_factory.param)) { + PJ_EINVAL; + } + attr->info.clock_rate = lyra_factory.param[idx].clock_rate; + attr->info.avg_bps = lyra_cfg.bit_rate; + attr->info.max_bps = LYRA_MAX_BITRATE; + + attr->info.pcm_bits_per_sample = 16; + attr->info.frm_ptime = 20; + attr->setting.frm_per_pkt = 1; + attr->setting.dec_fmtp.cnt = 1; + pj_strset2(&attr->setting.dec_fmtp.param[0].name, BIT_RATE_STR); + pj_strset2(&attr->setting.dec_fmtp.param[0].val, + get_bit_rate_val(lyra_cfg.bit_rate)); + + /* Default flags. */ + attr->setting.cng = 0; + attr->setting.plc = 0; + attr->setting.penh = 0; + attr->setting.vad = LYRA_DEFAULT_VAD; + + return PJ_SUCCESS; +} + +static pj_status_t lyra_enum_codecs(pjmedia_codec_factory *factory, + unsigned *count, + pjmedia_codec_info codecs[]) +{ + int i; + unsigned max_cnt; + + PJ_UNUSED_ARG(factory); + PJ_ASSERT_RETURN(factory == &lyra_factory.base, PJ_EINVAL); + PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL); + + max_cnt = *count; + *count = 0; + + for (i = PJ_ARRAY_SIZE(lyra_factory.param) - 1; + i >= 0 && *count < max_cnt; --i) + { + if (!lyra_factory.param[i].enabled) + continue; + + pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info)); + pj_strset2(&codecs[*count].encoding_name, LYRA_STR); + codecs[*count].pt = lyra_factory.param[i].pt; + codecs[*count].type = PJMEDIA_TYPE_AUDIO; + codecs[*count].clock_rate = lyra_factory.param[i].clock_rate; + codecs[*count].channel_cnt = 1; + + ++*count; + } + + return PJ_SUCCESS; +} + +static pj_status_t lyra_alloc_codec(pjmedia_codec_factory *factory, + const pjmedia_codec_info *id, + pjmedia_codec **p_codec) +{ + pj_pool_t *pool; + struct lyra_data *lyra_data; + pjmedia_codec *codec; + pj_status_t status; + + PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL); + PJ_ASSERT_RETURN(factory == &lyra_factory.base, PJ_EINVAL); + + pool = pjmedia_endpt_create_pool(lyra_factory.endpt, "lyra%p", 2000, 2000); + PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); + + lyra_data = PJ_POOL_ZALLOC_T(pool, struct lyra_data); + codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec); + + status = pj_mutex_create_simple(pool, "lyra_mutex", &lyra_data->mutex); + if (status != PJ_SUCCESS) { + pj_pool_release(pool); + return status; + } + + lyra_data->pool = pool; + lyra_data->dec_bit_rate = lyra_cfg.bit_rate; + + codec->op = &lyra_op; + codec->codec_data = lyra_data; + codec->factory = factory; + + *p_codec = codec; + return PJ_SUCCESS; +} + +static pj_status_t lyra_dealloc_codec(pjmedia_codec_factory *factory, + pjmedia_codec *codec) +{ + struct lyra_data *lyra_data; + + PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); + PJ_UNUSED_ARG(factory); + PJ_ASSERT_RETURN(factory == &lyra_factory.base, PJ_EINVAL); + + lyra_data = (struct lyra_data*)codec->codec_data; + if (lyra_data) { + pj_mutex_destroy(lyra_data->mutex); + lyra_data->mutex = NULL; + pj_pool_release(lyra_data->pool); + } + + return PJ_SUCCESS; +} + +/* Prototypes for lyra implementation. */ +static pj_status_t lyra_codec_init(pjmedia_codec *codec, + pj_pool_t *pool) +{ + PJ_UNUSED_ARG(codec); + PJ_UNUSED_ARG(pool); + return PJ_SUCCESS; +} + +static unsigned get_bit_rate_from_fmtp(pj_bool_t is_enc, + pjmedia_codec_param *attr) +{ + unsigned bit_rate = 0; + + pjmedia_codec_fmtp *fmtp = (is_enc?&attr->setting.enc_fmtp: + &attr->setting.dec_fmtp); + /* Get bit_rate from fmpt */ + for (unsigned i = 0; i < fmtp->cnt; ++i) { + if (pj_strcmp2(&fmtp->param[i].name, BIT_RATE_STR) == 0) + { + bit_rate = (pj_uint16_t) pj_strtoul(&fmtp->param[i].val); + break; + } + } + if (bit_rate == 0 || + (bit_rate != 3200 && bit_rate != 6000 && bit_rate != 9200)) + { + bit_rate = PJMEDIA_CODEC_LYRA_DEFAULT_BIT_RATE; + } + return bit_rate; +} + +static pj_status_t lyra_codec_open(pjmedia_codec *codec, + pjmedia_codec_param *attr) +{ + ghc::filesystem::path model_path = lyra_cfg.model_path.ptr; + struct lyra_data *lyra_data = (struct lyra_data*)codec->codec_data; + + pj_mutex_lock(lyra_data->mutex); + + lyra_data->enc_bit_rate = get_bit_rate_from_fmtp(PJ_TRUE, attr); + lyra_data->dec_bit_rate = get_bit_rate_from_fmtp(PJ_FALSE, attr); + + lyra_data->vad_enabled = (attr->setting.vad != 0); + lyra_data->plc_enabled = (attr->setting.plc != 0); + + PJ_LOG(4, (THIS_FILE, "Opening codec, model_path=%.*s, chan_cnt=%d, " + "enc_bit_rate=%d, dec_bit_rate=%d, clockrate=%d, vad=%d", + (int)lyra_cfg.model_path.slen, lyra_cfg.model_path.ptr, + attr->info.channel_cnt, lyra_data->enc_bit_rate, + lyra_data->dec_bit_rate, attr->info.clock_rate, + lyra_data->vad_enabled)); + + lyra_data->enc = LyraEncoder::Create(attr->info.clock_rate, + attr->info.channel_cnt, + lyra_data->enc_bit_rate, + lyra_data->vad_enabled, + model_path); + if (lyra_data->enc == nullptr) { + PJ_LOG(2, (THIS_FILE, "Could not create lyra encoder")); + pj_mutex_unlock(lyra_data->mutex); + return PJMEDIA_CODEC_EFAILED; + } + + lyra_data->dec = LyraDecoder::Create(attr->info.clock_rate, + attr->info.channel_cnt, + model_path); + if (lyra_data->dec == nullptr) { + PJ_LOG(2, (THIS_FILE, "Could not create lyra decoder")); + pj_mutex_unlock(lyra_data->mutex); + return PJMEDIA_CODEC_EFAILED; + } + lyra_data->samples_per_frame = + attr->info.clock_rate / lyra_data->enc->frame_rate(); + + PJ_LOG(4, (THIS_FILE, "Done opening codec")); + + pj_mutex_unlock(lyra_data->mutex); + return PJ_SUCCESS; +} + +static pj_status_t lyra_codec_close(pjmedia_codec *codec) +{ + struct lyra_data *lyra_data = (struct lyra_data*)codec->codec_data; + + /* Destroy encoder*/ + if (lyra_data->enc) { + lyra_data->enc.reset(); + } + + /* Destroy decoder */ + if (lyra_data->dec) { + lyra_data->dec.reset(); + } + + return PJ_SUCCESS; +} + +static pj_status_t lyra_codec_modify(pjmedia_codec *codec, + const pjmedia_codec_param *attr) +{ + struct lyra_data *lyra_data = (struct lyra_data*)codec->codec_data; + + lyra_data->plc_enabled = (attr->setting.plc != 0); + lyra_data->vad_enabled = (attr->setting.vad != 0); + + return PJ_SUCCESS; +} + +static pj_status_t lyra_codec_parse(pjmedia_codec *codec, + void *pkt, + pj_size_t pkt_size, + const pj_timestamp *ts, + unsigned *frame_cnt, + pjmedia_frame frames[]) +{ + unsigned count = 0; + struct lyra_data* lyra_data = (struct lyra_data*)codec->codec_data; + unsigned frm_size = lyra_data->dec_bit_rate / (50 * 8); + pj_size_t orig_pkt_size = pkt_size; + + PJ_UNUSED_ARG(codec); + + PJ_ASSERT_RETURN(ts && frame_cnt && frames, PJ_EINVAL); + + pj_mutex_lock(lyra_data->mutex); + while (pkt_size >= frm_size && count < *frame_cnt) { + frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO; + frames[count].buf = pkt; + frames[count].size = frm_size; + frames[count].timestamp.u64 = ts->u64 + + lyra_data->samples_per_frame * count; + pkt = ((char*)pkt) + frm_size; + pkt_size -= frm_size; + + ++count; + } + if (pkt_size != 0 && count == 1) { + frames[0].size = orig_pkt_size; + } + + *frame_cnt = count; + pj_mutex_unlock(lyra_data->mutex); + return PJ_SUCCESS; +} + +static pj_status_t lyra_codec_encode(pjmedia_codec *codec, + const struct pjmedia_frame *input, + unsigned output_buf_len, + struct pjmedia_frame *output) +{ + struct lyra_data *lyra_data = (struct lyra_data*)codec->codec_data; + unsigned samples_per_frame = lyra_data->samples_per_frame; + int i = 0; + pj_int16_t *pcm_in = (pj_int16_t*)input->buf; + pj_size_t in_size = input->size >> 1; + std::vector encoded_data; + + pj_mutex_lock(lyra_data->mutex); + + if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) { + output->size = 0; + output->buf = NULL; + output->timestamp = input->timestamp; + output->type = input->type; + pj_mutex_unlock(lyra_data->mutex); + return PJ_SUCCESS; + } + + for (int proc_size = 0; + proc_size + samples_per_frame <= in_size; + proc_size += samples_per_frame, ++i) + { + auto encoded = lyra_data->enc->Encode( + absl::MakeConstSpan(pcm_in+proc_size, + samples_per_frame)); + if (!encoded.has_value()) { + PJ_LOG(2, (THIS_FILE, + "Unable to encode starting at samples at byte %d.", + proc_size)); + pj_mutex_unlock(lyra_data->mutex); + return PJMEDIA_CODEC_EFAILED; + } + encoded_data.insert(encoded_data.end(), + encoded.value().begin(), + encoded.value().end()); + + } + PJ_ASSERT_ON_FAIL(encoded_data.size() <= output_buf_len, + { pj_mutex_unlock(lyra_data->mutex); + return PJMEDIA_CODEC_EFRMTOOSHORT; }); + + output->size = encoded_data.size(); + output->type = PJMEDIA_FRAME_TYPE_AUDIO; + output->timestamp = input->timestamp; + std::copy(encoded_data.begin(), encoded_data.end(), (char*)output->buf); + + pj_mutex_unlock(lyra_data->mutex); + return PJ_SUCCESS; +} + +static pj_status_t lyra_codec_decode(pjmedia_codec *codec, + const struct pjmedia_frame *input, + unsigned output_buf_len, + struct pjmedia_frame *output) +{ + struct lyra_data *lyra_data = (struct lyra_data*)codec->codec_data; + unsigned samples_per_frame = lyra_data->samples_per_frame; + unsigned samples_decoded = 0; + std::vector decoded_data; + + if (output_buf_len < (samples_per_frame << 1)) + return PJMEDIA_CODEC_EPCMTOOSHORT; + + pj_mutex_lock(lyra_data->mutex); + if (input) { + pj_uint8_t *in_data = (pj_uint8_t*)input->buf; + if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) { + pjmedia_zero_samples((pj_int16_t*)output->buf, samples_per_frame); + output->size = samples_per_frame << 1; + output->timestamp.u64 = input->timestamp.u64; + output->type = PJMEDIA_FRAME_TYPE_AUDIO; + pj_mutex_unlock(lyra_data->mutex); + return PJ_SUCCESS; + } + if (!lyra_data->dec->SetEncodedPacket( + absl::MakeConstSpan(in_data, input->size))) + { + PJ_LOG(4, (THIS_FILE, "Unable to set encoded packet")); + pj_mutex_unlock(lyra_data->mutex); + return PJMEDIA_CODEC_EFAILED; + } + } + + while (samples_decoded < samples_per_frame) { + unsigned samples_to_request = samples_per_frame - samples_decoded; + + auto decoded = lyra_data->dec->DecodeSamples(samples_to_request); + if (!decoded.has_value()) { + PJ_LOG(4, (THIS_FILE, "Decode failed!")); + pj_mutex_unlock(lyra_data->mutex); + return PJMEDIA_CODEC_EFAILED; + } + samples_decoded += (unsigned)decoded->size(); + decoded_data.insert(decoded_data.end(), decoded.value().begin(), + decoded.value().end()); + } + std::copy(decoded_data.begin(), decoded_data.end(), + (pj_int16_t*)output->buf); + output->type = PJMEDIA_FRAME_TYPE_AUDIO; + output->size = samples_per_frame << 1; + if (input) { + output->timestamp.u64 = input->timestamp.u64; + } + pj_mutex_unlock(lyra_data->mutex); + return PJ_SUCCESS; +} + +static pj_status_t lyra_codec_recover(pjmedia_codec *codec, + unsigned output_buf_len, + struct pjmedia_frame *output) +{ + struct lyra_data *lyra_data = (struct lyra_data*)codec->codec_data; + unsigned samples_per_frame = lyra_data->samples_per_frame; + pj_status_t status; + + /* output_buf_len is unreferenced when building in Release mode */ + PJ_UNUSED_ARG(output_buf_len); + + pj_assert(samples_per_frame <= output_buf_len / 2); + + status = lyra_codec_decode(codec, NULL, output_buf_len, output); + + output->size = samples_per_frame << 1; + + return status; +} + +PJ_DEF(pj_status_t) +pjmedia_codec_lyra_get_config(pjmedia_codec_lyra_config *cfg) +{ + PJ_ASSERT_RETURN(cfg, PJ_EINVAL); + + pj_memcpy(cfg, &lyra_cfg, sizeof(pjmedia_codec_lyra_config)); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) +pjmedia_codec_lyra_set_config(const pjmedia_codec_lyra_config *cfg) +{ + if (cfg->bit_rate != 3200 && cfg->bit_rate != 6000 && + cfg->bit_rate != 9200) + { + return PJ_EINVAL; + } + lyra_cfg.bit_rate = cfg->bit_rate; + pj_strncpy_with_null(&lyra_cfg.model_path, &cfg->model_path, + PJ_ARRAY_SIZE(lyra_factory.model_path_buf)); + return PJ_SUCCESS; +} + +#if defined(_MSC_VER) +# pragma comment(lib, "liblyra") +#endif + +#endif diff --git a/pjsip/include/pjsua2/endpoint.hpp b/pjsip/include/pjsua2/endpoint.hpp index a40c38df57..5c78de026e 100644 --- a/pjsip/include/pjsua2/endpoint.hpp +++ b/pjsip/include/pjsua2/endpoint.hpp @@ -1794,6 +1794,21 @@ class Endpoint void setCodecOpusConfig(const CodecOpusConfig &opus_cfg) PJSUA2_THROW(Error); + /** + * Get codec Lyra config. + * + */ + CodecLyraConfig getCodecLyraConfig() const PJSUA2_THROW(Error); + + /** + * Set codec Lyra config. + * + * @param lyra_cfg Codec Lyra configuration. + * + */ + void setCodecLyraConfig(const CodecLyraConfig &lyra_cfg) + PJSUA2_THROW(Error); + /** * Enumerate all SRTP crypto-suite names. * diff --git a/pjsip/include/pjsua2/media.hpp b/pjsip/include/pjsua2/media.hpp index ef9edde9d0..0d1631908f 100644 --- a/pjsip/include/pjsua2/media.hpp +++ b/pjsip/include/pjsua2/media.hpp @@ -2641,6 +2641,36 @@ struct CodecOpusConfig void fromPj(const pjmedia_codec_opus_config &config); }; +/** + * Lyra codec setting; + */ +struct CodecLyraConfig +{ + /** + * The value represents the decoder bitrate requested by the receiver. + * Endpoints can be configured with different bitrates. For example, + * the local endpoint might be set to a bitrate of 3200, while + * the remote endpoint is set to 6000. In this scenario, the remote + * endpoint will send data at 3200 bitrate, while the local endpoint + * will send data at 6000 bitrate. Valid bitrate: 3200, 6000, 9200. + * By default it is set to PJMEDIA_CODEC_LYRA_DEFAULT_BIT_RATE. + */ + unsigned bitRate; + + /** + * Lyra required some additional (model) files, including + * \b lyra_config.binarypb , \b lyragan.tflite , \b quantizer.tflite and + * \b soundstream_encoder.tflite . + * This setting represents the folder containing the above files. + * The specified folder should contain these files. If an invalid folder + * is provided, the codec creation will fail. + */ + string modelPath; + + pjmedia_codec_lyra_config toPj() const; + void fromPj(const pjmedia_codec_lyra_config &config); +}; + /** * Detailed codec attributes used in configuring a video codec and in querying * the capability of video codec factories. diff --git a/pjsip/src/pjsua2/endpoint.cpp b/pjsip/src/pjsua2/endpoint.cpp index 417a123cfd..487b095166 100644 --- a/pjsip/src/pjsua2/endpoint.cpp +++ b/pjsip/src/pjsua2/endpoint.cpp @@ -2526,6 +2526,36 @@ void Endpoint::setCodecOpusConfig(const CodecOpusConfig &opus_cfg) #endif } +CodecLyraConfig Endpoint::getCodecLyraConfig() const PJSUA2_THROW(Error) +{ + CodecLyraConfig config; +#if defined(PJMEDIA_HAS_LYRA_CODEC) && (PJMEDIA_HAS_LYRA_CODEC!=0) + pjmedia_codec_lyra_config lyra_cfg; + + PJSUA2_CHECK_EXPR(pjmedia_codec_lyra_get_config(&lyra_cfg)); + config.fromPj(lyra_cfg); +#else + PJSUA2_RAISE_ERROR(PJ_ENOTSUP); +#endif + + return config; +} + +void Endpoint::setCodecLyraConfig(const CodecLyraConfig &lyra_cfg) + PJSUA2_THROW(Error) +{ +#if defined(PJMEDIA_HAS_LYRA_CODEC) && (PJMEDIA_HAS_LYRA_CODEC!=0) + pjmedia_codec_lyra_config new_lyra_cfg; + new_lyra_cfg = lyra_cfg.toPj(); + + PJSUA2_CHECK_EXPR(pjmedia_codec_lyra_set_config(&new_lyra_cfg)); +#else + PJ_UNUSED_ARG(lyra_cfg); + + PJSUA2_RAISE_ERROR(PJ_ENOTSUP); +#endif +} + void Endpoint::clearCodecInfoList(CodecInfoVector &codec_list) { for (unsigned i=0;ionFrameRequested(frame_); frame->type = frame_.type; frame->size = PJ_MIN(frame_.buf.size(), frame_.size); - -#if ((defined(_MSVC_LANG) && _MSVC_LANG <= 199711L) || __cplusplus <= 199711L) - /* C++98 does not have Vector::data() */ - if (frame->size > 0) + +#if ((defined(_MSVC_LANG) && _MSVC_LANG <= 199711L) || __cplusplus <= 199711L) + /* C++98 does not have Vector::data() */ + if (frame->size > 0) pj_memcpy(frame->buf, &frame_.buf[0], frame->size); #else - /* Newer than C++98 */ + /* Newer than C++98 */ pj_memcpy(frame->buf, frame_.buf.data(), frame->size); -#endif +#endif return PJ_SUCCESS; @@ -2032,6 +2032,22 @@ void CodecOpusConfig::fromPj(const pjmedia_codec_opus_config &config) cbr = PJ2BOOL(config.cbr); } +pjmedia_codec_lyra_config CodecLyraConfig::toPj() const +{ + pjmedia_codec_lyra_config config; + + config.bit_rate = bitRate; + config.model_path = str2Pj(modelPath); + + return config; +} + +void CodecLyraConfig::fromPj(const pjmedia_codec_lyra_config &config) +{ + bitRate = config.bit_rate; + modelPath = pj2Str(config.model_path); +} + /////////////////////////////////////////////////////////////////////////////// void VidCodecParam::fromPj(const pjmedia_vid_codec_param ¶m) { From 77f8ad5d5429f59c64fa7ffbedb64a7ced7914b1 Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 19 Jun 2024 11:35:09 +0800 Subject: [PATCH 030/491] Fixed WebRTC AEC&AEC3 build for ARMv7 (#3992) --- aconfigure | 4 ++-- aconfigure.ac | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aconfigure b/aconfigure index 77346d94da..47fce480ee 100755 --- a/aconfigure +++ b/aconfigure @@ -10340,7 +10340,7 @@ printf "%s\n" "Checking if libwebrtc is disabled...no" >&6; } case $target in armv7l*gnueabihf) ac_webrtc_instset=neon - ac_webrtc_cflags="-DWEBRTC_ARCH_ARMV7 -mfloat-abi=hard -mfpu=neon" + ac_webrtc_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" ;; arm-apple-darwin* | aarch64*) ac_webrtc_instset=neon @@ -10458,7 +10458,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu case $target in armv7l*gnueabihf) ac_webrtc_aec3_instset=neon - ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARMV7 -mfloat-abi=hard -mfpu=neon" + ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" ;; arm-apple-darwin* | aarch64*) ac_webrtc_aec3_instset=neon diff --git a/aconfigure.ac b/aconfigure.ac index cea9d52090..df91fbe0e2 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -2482,7 +2482,7 @@ AC_ARG_ENABLE(libwebrtc, case $target in armv7l*gnueabihf) ac_webrtc_instset=neon - ac_webrtc_cflags="-DWEBRTC_ARCH_ARMV7 -mfloat-abi=hard -mfpu=neon" + ac_webrtc_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" ;; arm-apple-darwin* | aarch64*) ac_webrtc_instset=neon @@ -2569,7 +2569,7 @@ AC_ARG_ENABLE(libwebrtc_aec3, case $target in armv7l*gnueabihf) ac_webrtc_aec3_instset=neon - ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARMV7 -mfloat-abi=hard -mfpu=neon" + ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" ;; arm-apple-darwin* | aarch64*) ac_webrtc_aec3_instset=neon From cd89bb60a02a6d7aaddbc052d92d24f4e4162cf2 Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 19 Jun 2024 11:36:09 +0800 Subject: [PATCH 031/491] Add lock to pjsip resolver (#3991) --- pjsip/src/pjsip/sip_resolve.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pjsip/src/pjsip/sip_resolve.c b/pjsip/src/pjsip/sip_resolve.c index a85cc1afd0..eebf47e45a 100644 --- a/pjsip/src/pjsip/sip_resolve.c +++ b/pjsip/src/pjsip/sip_resolve.c @@ -51,6 +51,7 @@ struct query pjsip_resolver_callback *cb; pj_dns_async_query *object; pj_dns_async_query *object6; + pj_grp_lock_t *grp_lock; pj_status_t last_error; /* Original request: */ @@ -71,6 +72,7 @@ struct query struct pjsip_resolver_t { pj_dns_resolver *res; + pj_grp_lock_t *grp_lock; pjsip_ext_resolver *ext_res; }; @@ -93,9 +95,18 @@ PJ_DEF(pj_status_t) pjsip_resolver_create( pj_pool_t *pool, pjsip_resolver_t **p_res) { pjsip_resolver_t *resolver; + pj_status_t status; PJ_ASSERT_RETURN(pool && p_res, PJ_EINVAL); resolver = PJ_POOL_ZALLOC_T(pool, pjsip_resolver_t); + + /* Create group lock */ + status = pj_grp_lock_create(pool, NULL, &resolver->grp_lock); + if (status != PJ_SUCCESS) + return status; + + pj_grp_lock_add_ref(resolver->grp_lock); + *p_res = resolver; return PJ_SUCCESS; @@ -160,6 +171,11 @@ PJ_DEF(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver) #endif resolver->res = NULL; } + + if (resolver->grp_lock) { + pj_grp_lock_dec_ref(resolver->grp_lock); + resolver->grp_lock = NULL; + } } /* @@ -393,6 +409,7 @@ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, query->objname = THIS_FILE; query->token = token; query->cb = cb; + query->grp_lock = resolver->grp_lock; query->req.target = *target; pj_strdup(pool, &query->req.target.addr.host, &target->addr.host); @@ -532,6 +549,8 @@ static void dns_a_callback(void *user_data, struct query *query = (struct query*) user_data; pjsip_server_addresses *srv = &query->server; + pj_grp_lock_acquire(query->grp_lock); + /* Reset outstanding job */ query->object = NULL; @@ -577,6 +596,8 @@ static void dns_a_callback(void *user_data, else (*query->cb)(query->last_error, query->token, NULL); } + + pj_grp_lock_release(query->grp_lock); } @@ -590,6 +611,8 @@ static void dns_aaaa_callback(void *user_data, struct query *query = (struct query*) user_data; pjsip_server_addresses *srv = &query->server; + pj_grp_lock_acquire(query->grp_lock); + /* Reset outstanding job */ query->object6 = NULL; @@ -636,6 +659,8 @@ static void dns_aaaa_callback(void *user_data, else (*query->cb)(query->last_error, query->token, NULL); } + + pj_grp_lock_release(query->grp_lock); } From 9cca8e69f59085e3dd0f79f6ecd96c3d7aba16ed Mon Sep 17 00:00:00 2001 From: Andreas Wehrmann Date: Wed, 19 Jun 2024 09:59:41 +0200 Subject: [PATCH 032/491] pj_dns_srv_resolve() returns query object when it shouldn't (#3990) --- pjlib-util/include/pjlib-util/resolver.h | 2 +- pjlib-util/include/pjlib-util/srv_resolver.h | 2 +- pjlib-util/src/pjlib-util/srv_resolver.c | 7 +++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pjlib-util/include/pjlib-util/resolver.h b/pjlib-util/include/pjlib-util/resolver.h index 5ea01aca73..000f6a55eb 100644 --- a/pjlib-util/include/pjlib-util/resolver.h +++ b/pjlib-util/include/pjlib-util/resolver.h @@ -406,7 +406,7 @@ PJ_DECL(pj_status_t) pj_dns_resolver_destroy(pj_dns_resolver *resolver, * @param user_data Arbitrary user data to be associated with the query, * and which will be given back in the callback. * @param p_query Optional pointer to receive the query object, if one - * was started. If this pointer is specified, a NULL may + * was started. If this pointer is specified, a NULL will * be returned if response cache is available immediately. * * @return PJ_SUCCESS if either an asynchronous query has been diff --git a/pjlib-util/include/pjlib-util/srv_resolver.h b/pjlib-util/include/pjlib-util/srv_resolver.h index 1f92cb588d..4856e39bea 100644 --- a/pjlib-util/include/pjlib-util/srv_resolver.h +++ b/pjlib-util/include/pjlib-util/srv_resolver.h @@ -181,7 +181,7 @@ typedef void pj_dns_srv_resolver_cb(void *user_data, * @param cb Pointer to callback function to receive the * notification when the resolution process completes. * @param p_query Optional pointer to receive the query object, if one - * was started. If this pointer is specified, a NULL may + * was started. If this pointer is specified, a NULL will * be returned if response cache is available immediately. * * @return PJ_SUCCESS on success, or the appropriate error code. diff --git a/pjlib-util/src/pjlib-util/srv_resolver.c b/pjlib-util/src/pjlib-util/srv_resolver.c index 0a591632dc..bd1babc1ed 100644 --- a/pjlib-util/src/pjlib-util/srv_resolver.c +++ b/pjlib-util/src/pjlib-util/srv_resolver.c @@ -107,7 +107,7 @@ PJ_DEF(pj_status_t) pj_dns_srv_resolve( const pj_str_t *domain_name, { pj_size_t len; pj_str_t target_name; - pj_dns_srv_async_query *query_job; + pj_dns_srv_async_query *query_job, *p_q = NULL; pj_status_t status; PJ_ASSERT_RETURN(domain_name && domain_name->slen && @@ -156,8 +156,11 @@ PJ_DEF(pj_status_t) pj_dns_srv_resolve( const pj_str_t *domain_name, query_job->dns_state, 0, &dns_callback, query_job, &query_job->q_srv); + if (query_job->q_srv) + p_q = query_job; + if (status==PJ_SUCCESS && p_query) - *p_query = query_job; + *p_query = p_q; return status; } From ed52054c1584c9b2f354d74b2d48b42ad3a9a1cd Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 19 Jun 2024 16:43:30 +0800 Subject: [PATCH 033/491] Replace Trac links in the doc (#3995) --- pjlib/include/pj/lock.h | 4 +++- pjmedia/include/pjmedia/config.h | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pjlib/include/pj/lock.h b/pjlib/include/pj/lock.h index ccf953fa8f..334c1e1284 100644 --- a/pjlib/include/pj/lock.h +++ b/pjlib/include/pj/lock.h @@ -177,7 +177,9 @@ PJ_DECL(pj_status_t) pj_lock_destroy( pj_lock_t *lock ); * - must be able to synchronize with external lock (for example, a dialog * lock must be able to sync itself with PJSUA lock) * - * Please see https://trac.pjsip.org/repos/wiki/Group_Lock for more info. + * Please see + * https://docs.pjsip.org/en/latest/specific-guides/develop/group_lock.html + * for more info. */ /** diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h index 97e4a53bc1..4325858b6a 100644 --- a/pjmedia/include/pjmedia/config.h +++ b/pjmedia/include/pjmedia/config.h @@ -975,8 +975,8 @@ /** * Specify the tone generator algorithm to be used. Please see - * http://trac.pjsip.org/repos/wiki/Tone_Generator for the performance - * analysis results of the various tone generator algorithms. + * https://docs.pjsip.org/en/latest/specific-guides/media/tonegen.html for + * the performance analysis results of the various tone generator algorithms. * * Default value: * - PJMEDIA_TONEGEN_FLOATING_POINT when PJ_HAS_FLOATING_POINT is set From 3249dfba39196bbbe3fc9d39362081fc118f5822 Mon Sep 17 00:00:00 2001 From: Andreas Wehrmann Date: Thu, 20 Jun 2024 07:21:33 +0200 Subject: [PATCH 034/491] make sure to return NULL for the query object when a response is returned from the cache (#3996) --- pjlib-util/src/pjlib-util/resolver.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pjlib-util/src/pjlib-util/resolver.c b/pjlib-util/src/pjlib-util/resolver.c index 1ec8daace4..7e76ebcd94 100644 --- a/pjlib-util/src/pjlib-util/resolver.c +++ b/pjlib-util/src/pjlib-util/resolver.c @@ -917,6 +917,12 @@ PJ_DEF(pj_status_t) pj_dns_resolver_start_query( pj_dns_resolver *resolver, cache->ref_cnt++; pj_grp_lock_release(resolver->grp_lock); + /* Since we're returning an answer from cache, + * there is no query object to return. + */ + if (p_query) + *p_query = NULL; + /* This cached response is still valid. Just return this * response to caller. */ From 8293af93f9bf3ae31f7dc9f0ae3d5a24d2797087 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Mon, 24 Jun 2024 12:47:36 +0700 Subject: [PATCH 035/491] Fix bug when writing empty string to log (#3998) --- pjlib/src/pj/log.c | 4 +- pjlib/src/pjlib-test/os.c | 139 ++++++++++++++++++++++++++++++++++++ pjlib/src/pjlib-test/test.c | 1 + pjlib/src/pjlib-test/test.h | 1 + 4 files changed, 143 insertions(+), 2 deletions(-) diff --git a/pjlib/src/pj/log.c b/pjlib/src/pj/log.c index db118118a0..df0544eb41 100644 --- a/pjlib/src/pj/log.c +++ b/pjlib/src/pj/log.c @@ -468,11 +468,11 @@ PJ_DEF(void) pj_log( const char *sender, int level, print_len = pj_ansi_snprintf(pre, sizeof(log_buffer)-len, ""); } - if (print_len < 1 || print_len >= (int)(sizeof(log_buffer)-len)) { + if (print_len < 0 || print_len >= (int)(sizeof(log_buffer)-len)) { print_len = sizeof(log_buffer) - len - 1; } len = len + print_len; - if (len > 0 && len < (int)sizeof(log_buffer)-2) { + if (len >= 0 && len < (int)sizeof(log_buffer)-2) { if (log_decor & PJ_LOG_HAS_CR) { log_buffer[len++] = '\r'; } diff --git a/pjlib/src/pjlib-test/os.c b/pjlib/src/pjlib-test/os.c index 3bc1de8ebb..ec16577482 100644 --- a/pjlib/src/pjlib-test/os.c +++ b/pjlib/src/pjlib-test/os.c @@ -19,6 +19,10 @@ #include "test.h" #include #include +#include +#include + +#define THIS_FILE "os.c" #if INCLUDE_OS_TEST static int endianness_test32(void) @@ -90,6 +94,141 @@ static int endianness_test32(void) return 0; } +static int log_written; +static char test_large_msg[PJ_LOG_MAX_SIZE]; + +static void log_write(int level, const char *buffer, int len) +{ + log_written = len; + //printf("%s", buffer); +} + +int log_test(void) +{ + pj_log_func *old_func = pj_log_get_log_func(); + struct log_test_t { + const char *title; + unsigned decor; + const char *fmt; + const char *arg; + int min_len; + int max_len; + } log_tests[] = { + /* String lengths: + * PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC: 13 + * PJ_LOG_HAS_SENDER: PJ_LOG_SENDER_WIDTH+1 + * "Hello world!": 12 + * PJ_LOG_HAS_NEWLINE: 1 + */ + { + "normal log", + PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC | + PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE, + "Hello %s!", + "world", + 13+PJ_LOG_SENDER_WIDTH+1+12+1, + 13+PJ_LOG_SENDER_WIDTH+1+12+1, + }, + { + "normal log with no decor", + 0, + "Hello %s!", + "world", + 12, + 12, + }, + { + "normal log with just newline", + PJ_LOG_HAS_NEWLINE, + "Hello %s!", + "world", + 13, + 13, + }, + { + "empty string with normal decor", + PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC | + PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE, + "%s", + "", + 13+PJ_LOG_SENDER_WIDTH+1+1, + 13+PJ_LOG_SENDER_WIDTH+1+1, + }, + { + "empty string with nodecor", + 0, + "%s", + "", + 0, + 0, + }, + { + "empty string with just newline", + PJ_LOG_HAS_NEWLINE, + "%s", + "", + 1, + 1, + }, + { + "large message with normal decor", + PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC | + PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE, + "%s", + test_large_msg, + PJ_LOG_MAX_SIZE-1, + PJ_LOG_MAX_SIZE-1, + }, + { + "large message with no decor", + 0, + "%s", + test_large_msg, + PJ_LOG_MAX_SIZE-1, + PJ_LOG_MAX_SIZE-1, + }, + { + "large message with just newline", + PJ_LOG_HAS_NEWLINE, + "%s", + test_large_msg, + PJ_LOG_MAX_SIZE-1, + PJ_LOG_MAX_SIZE-1, + }, + }; + unsigned old_decor = pj_log_get_decor(); + unsigned i; + + memset(test_large_msg, 'A', sizeof(test_large_msg)); + test_large_msg[sizeof(test_large_msg)-1] = '\0'; + + pj_log_set_log_func( &log_write ); + + for (i=0; idecor); + + PJ_LOG(1,(THIS_FILE, t->fmt, t->arg)); + + if (log_written < t->min_len || log_written > t->max_len) { + pj_log_set_log_func( old_func ); + pj_log_set_decor(old_decor); + + PJ_LOG(1,(THIS_FILE, + " Error: in test %d (%s), writing (\"%s\", \"%s\"), expecting %d<=len<=%d, got len=%d", + i, t->title, t->fmt, t->arg, t->min_len, t->max_len, log_written)); + return 33; + } + } + + pj_log_set_log_func( old_func ); + pj_log_set_decor(old_decor); + + return 0; +} + int os_test(void) { const pj_sys_info *si; diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c index 4bc8d5b3bc..9038bcbec8 100644 --- a/pjlib/src/pjlib-test/test.c +++ b/pjlib/src/pjlib-test/test.c @@ -89,6 +89,7 @@ int test_inner(void) #endif #if INCLUDE_OS_TEST + DO_TEST( log_test() ); DO_TEST( os_test() ); #endif diff --git a/pjlib/src/pjlib-test/test.h b/pjlib/src/pjlib-test/test.h index 24d7e29f0c..b527de7efd 100644 --- a/pjlib/src/pjlib-test/test.h +++ b/pjlib/src/pjlib-test/test.h @@ -87,6 +87,7 @@ extern int exception_test(void); extern int rand_test(void); extern int list_test(void); extern int hash_test(void); +extern int log_test(void); extern int os_test(void); extern int pool_test(void); extern int pool_perf_test(void); From 8c8675d85cae8771b77804291480fd88b10cff22 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Mon, 24 Jun 2024 12:48:15 +0700 Subject: [PATCH 036/491] Add API to retrieve DNS server bound address to allow specifying zero as port number (#3999) --- pjlib-util/include/pjlib-util/dns_server.h | 14 +++++++++++++- pjlib-util/src/pjlib-util/dns_server.c | 13 ++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/pjlib-util/include/pjlib-util/dns_server.h b/pjlib-util/include/pjlib-util/dns_server.h index 25d2b30e79..f1a8dfda08 100644 --- a/pjlib-util/include/pjlib-util/dns_server.h +++ b/pjlib-util/include/pjlib-util/dns_server.h @@ -50,7 +50,8 @@ typedef struct pj_dns_server pj_dns_server; * registered to. * @param af Address family of the server socket (valid values * are pj_AF_INET() for IPv4 and pj_AF_INET6() for IPv6). - * @param port The UDP port to listen. + * @param port The UDP port to listen. Specify zero to bind to any + * port. * @param flags Flags, currently must be zero. * @param p_srv Pointer to receive the DNS server instance. * @@ -65,6 +66,17 @@ PJ_DECL(pj_status_t) pj_dns_server_create(pj_pool_factory *pf, unsigned flags, pj_dns_server **p_srv); +/** + * Get the DNS server address + * + * @param srv The DNS server instance. + * @param addr It will be filled with the server's bound address. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_dns_server_get_addr(pj_dns_server *srv, + pj_sockaddr *bound_addr); + /** * Destroy DNS server instance. * diff --git a/pjlib-util/src/pjlib-util/dns_server.c b/pjlib-util/src/pjlib-util/dns_server.c index 3231cb7d27..f3f7424d22 100644 --- a/pjlib-util/src/pjlib-util/dns_server.c +++ b/pjlib-util/src/pjlib-util/dns_server.c @@ -52,6 +52,7 @@ struct pj_dns_server pj_pool_t *pool; pj_pool_factory *pf; pj_activesock_t *asock; + pj_sockaddr bound_addr; pj_ioqueue_op_key_t send_key; struct rr rr_list; }; @@ -97,7 +98,8 @@ PJ_DEF(pj_status_t) pj_dns_server_create( pj_pool_factory *pf, pj_activesock_cfg_default(&sock_cfg); status = pj_activesock_create_udp(pool, &sock_addr, &sock_cfg, ioqueue, - &sock_cb, srv, &srv->asock, NULL); + &sock_cb, srv, &srv->asock, + &srv->bound_addr); if (status != PJ_SUCCESS) goto on_error; @@ -116,6 +118,15 @@ PJ_DEF(pj_status_t) pj_dns_server_create( pj_pool_factory *pf, } +PJ_DEF(pj_status_t) pj_dns_server_get_addr(pj_dns_server *srv, + pj_sockaddr *bound_addr) +{ + PJ_ASSERT_RETURN(srv && bound_addr, PJ_EINVAL); + pj_memcpy(bound_addr, &srv->bound_addr, sizeof(*bound_addr)); + return PJ_SUCCESS; +} + + PJ_DEF(pj_status_t) pj_dns_server_destroy(pj_dns_server *srv) { PJ_ASSERT_RETURN(srv, PJ_EINVAL); From b7d125bff8ecd259c948e00563578114544f5613 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Tue, 2 Jul 2024 18:10:03 +0700 Subject: [PATCH 037/491] Fix assertion in testing SIP resolve with loop-dgram tp_selector (#4006) Fix assertion in testing when tp_selector is used with loop-dgram transport and SIP dns resolver is configured --- pjsip/src/pjsip/sip_resolve.c | 2 + pjsip/src/test/test.c | 6 ++ pjsip/src/test/transport_loop_test.c | 89 ++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+) diff --git a/pjsip/src/pjsip/sip_resolve.c b/pjsip/src/pjsip/sip_resolve.c index eebf47e45a..bd7942415f 100644 --- a/pjsip/src/pjsip/sip_resolve.c +++ b/pjsip/src/pjsip/sip_resolve.c @@ -440,6 +440,8 @@ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, query->naptr[0].res_type = pj_str("_sip._tcp."); else if (type == PJSIP_TRANSPORT_UDP || type == PJSIP_TRANSPORT_UDP6) query->naptr[0].res_type = pj_str("_sip._udp."); + else if (type == PJSIP_TRANSPORT_LOOP_DGRAM) + query->naptr[0].res_type = pj_str("_sip._udp."); else { pj_assert(!"Unknown transport type"); query->naptr[0].res_type = pj_str("_sip._udp."); diff --git a/pjsip/src/test/test.c b/pjsip/src/test/test.c index 37c4dabec0..cda6d3c7aa 100644 --- a/pjsip/src/test/test.c +++ b/pjsip/src/test/test.c @@ -441,6 +441,12 @@ int test_main(char *testlist) } #endif +#if INCLUDE_LOOP_TEST + /* repeat again after resolver is configured in resolve_test() */ + if (SHOULD_RUN_TEST(include_loop_test)) { + DO_TEST(transport_loop_test()); + } +#endif #if INCLUDE_TSX_TEST if (SHOULD_RUN_TEST(include_tsx_test)) { diff --git a/pjsip/src/test/transport_loop_test.c b/pjsip/src/test/transport_loop_test.c index 63b346439a..42c8ed2543 100644 --- a/pjsip/src/test/transport_loop_test.c +++ b/pjsip/src/test/transport_loop_test.c @@ -23,6 +23,90 @@ #define THIS_FILE "transport_loop_test.c" +static void send_cb(pjsip_send_state *st, pj_ssize_t sent, pj_bool_t *cont) +{ + int *loop_resolve_status = (int*)st->token; + if (sent < 0) { + /* Success! */ + *loop_resolve_status = (int)-sent; + } else { + PJ_LOG(3,(THIS_FILE, + " error: expecting error in send_cb() but got sent=%ld", + sent)); + *loop_resolve_status = 0; + } +} + +static int loop_resolve_error() +{ + pjsip_transport *loop = NULL; + pj_sockaddr_in addr; + pj_str_t url; + pjsip_tx_data *tdata; + pjsip_tpselector tp_sel; + int i, loop_resolve_status; + pj_status_t status; + + loop_resolve_status = 0; + pj_bzero(&addr, sizeof(addr)); + + status = pjsip_endpt_acquire_transport( endpt, PJSIP_TRANSPORT_LOOP_DGRAM, + &addr, sizeof(addr), NULL, &loop); + if (status != PJ_SUCCESS) { + app_perror(" error: loop transport is not configured", status); + return -210; + } + + url = pj_str("sip:bob@unresolved-host"); + + for (i=0; i<2; ++i) { + /* variant a: without tp_selector */ + status = pjsip_endpt_create_request( endpt, &pjsip_options_method, + &url, &url, &url, NULL, NULL, -1, + NULL, &tdata); + if (status != PJ_SUCCESS) { + app_perror(" Error: unable to create request", status); + loop_resolve_status = -220; + goto on_return; + } + + if (i==1) { + /* variant b: with tp_selector */ + pj_bzero(&tp_sel, sizeof(tp_sel)); + tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; + tp_sel.u.transport = loop; + + pjsip_tx_data_set_transport(tdata, &tp_sel); + } + loop_resolve_status = PJ_EPENDING; + status = pjsip_endpt_send_request_stateless(endpt, tdata, + &loop_resolve_status, + &send_cb); + if (status!=PJ_EPENDING && status!=PJ_SUCCESS) { + /* Success! (we're expecting error and got immediate error)*/ + loop_resolve_status = 0; + } else { + flush_events(500); + if (loop_resolve_status!=PJ_EPENDING && loop_resolve_status!=PJ_SUCCESS) { + /* Success! (we're expecting error in callback)*/ + //PJ_PERROR(3, (THIS_FILE, loop_resolve_status, + // " correctly got error")); + loop_resolve_status = 0; + } else { + PJ_LOG(3,(THIS_FILE, " error: expecting error but status=%d", status)); + loop_resolve_status = -240; + goto on_return; + } + } + } + +on_return: + if (loop) + pjsip_transport_dec_ref(loop); + return loop_resolve_status; + +} + static int datagram_loop_test() { enum { LOOP = 8 }; @@ -61,6 +145,11 @@ static int datagram_loop_test() return status; } + /* Resolve error */ + status = loop_resolve_error(); + if (status) + return status; + min_rtt = 0xFFFFFFF; for (i=0; i Date: Wed, 3 Jul 2024 09:45:41 +0700 Subject: [PATCH 038/491] Fix coverity warning (#4003) * Fix coverity warning * Fix incorrect data supplied to pj_sockaddr_has_addr() * Change the return value to maintain the behavior --- pjlib/src/pjlib-test/ioq_tcp.c | 2 +- pjmedia/src/pjmedia/endpoint.c | 6 ++++-- pjmedia/src/pjmedia/stream.c | 2 +- pjmedia/src/pjmedia/vid_stream.c | 2 +- pjsip/src/pjsip-ua/sip_100rel.c | 6 ++++++ 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/pjlib/src/pjlib-test/ioq_tcp.c b/pjlib/src/pjlib-test/ioq_tcp.c index e3410dda74..3f665b4cc2 100644 --- a/pjlib/src/pjlib-test/ioq_tcp.c +++ b/pjlib/src/pjlib-test/ioq_tcp.c @@ -887,7 +887,7 @@ static int compliance_test_2(const pj_ioqueue_cfg *cfg) if (client[i].key != NULL) { pj_ioqueue_unregister(client[i].key); client[i].key = NULL; - server[i].sock = PJ_INVALID_SOCKET; + client[i].sock = PJ_INVALID_SOCKET; } else if (client[i].sock != PJ_INVALID_SOCKET) { pj_sock_close(client[i].sock); client[i].sock = PJ_INVALID_SOCKET; diff --git a/pjmedia/src/pjmedia/endpoint.c b/pjmedia/src/pjmedia/endpoint.c index c752c397ee..b9fe6b642b 100644 --- a/pjmedia/src/pjmedia/endpoint.c +++ b/pjmedia/src/pjmedia/endpoint.c @@ -803,8 +803,10 @@ pjmedia_endpt_create_video_sdp(pjmedia_endpt *endpt, continue; } - pjmedia_vid_codec_mgr_get_default_param(NULL, &codec_info[i], - &codec_param); + status = pjmedia_vid_codec_mgr_get_default_param(NULL, &codec_info[i], + &codec_param); + if (status != PJ_SUCCESS) + return status; fmt = &m->desc.fmt[m->desc.fmt_count++]; fmt->ptr = (char*) pj_pool_alloc(pool, 8); diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index b09a42ec33..d5d62aa431 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -2895,7 +2895,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, pj_sockaddr_cp(&stream->rem_rtp_addr, &info->rem_addr); if (stream->si.rtcp_mux) { pj_sockaddr_cp(&att_param.rem_rtcp, &info->rem_addr); - } else if (pj_sockaddr_has_addr(&info->rem_rtcp.addr)) { + } else if (pj_sockaddr_has_addr(&info->rem_rtcp)) { pj_sockaddr_cp(&att_param.rem_rtcp, &info->rem_rtcp); } att_param.addr_len = pj_sockaddr_get_len(&info->rem_addr); diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c index 8e0d9c8272..a109bfb1e2 100644 --- a/pjmedia/src/pjmedia/vid_stream.c +++ b/pjmedia/src/pjmedia/vid_stream.c @@ -2044,7 +2044,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( pj_sockaddr_cp(&stream->rem_rtp_addr, &info->rem_addr); if (info->rtcp_mux) { pj_sockaddr_cp(&att_param.rem_rtcp, &info->rem_addr); - } else if (pj_sockaddr_has_addr(&info->rem_rtcp.addr)) { + } else if (pj_sockaddr_has_addr(&info->rem_rtcp)) { pj_sockaddr_cp(&att_param.rem_rtcp, &info->rem_rtcp); } att_param.addr_len = pj_sockaddr_get_len(&info->rem_addr); diff --git a/pjsip/src/pjsip-ua/sip_100rel.c b/pjsip/src/pjsip-ua/sip_100rel.c index 3cfd8dcc4e..d253c8bceb 100644 --- a/pjsip/src/pjsip-ua/sip_100rel.c +++ b/pjsip/src/pjsip-ua/sip_100rel.c @@ -269,6 +269,12 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv, } rseq = (pj_uint32_t) pj_strtoul(&rseq_hdr->hvalue); + if (rseq < 1) { + PJ_LOG(4, (dd->inv->dlg->obj_name, + "Ignoring 100rel response RSeq header value less than 1")); + return PJ_EIGNORED; + } + /* Find UAC state for the specified call leg */ uac_state = dd->uac_state_list; while (uac_state) { From e9a8a69efc3b07b82f516f93d49332d2fb09f45e Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Tue, 9 Jul 2024 13:25:24 +0700 Subject: [PATCH 039/491] Unit test framework (#4007) * Merged unit-test and argparse features from unittest-framework branch * Further merge from unittest-framework branch and tested on Visial Studio 2015 * Fix accidental typo in test.h * Another typo fix to test.h * Remove argparse (into separate PR) * Remove argparse from old VC project * Remove pj_fifobuf_unalloc(), improve doc, improve error codes --- pjlib/build/Makefile | 4 +- pjlib/build/pjlib.vcproj | 116 ++++ pjlib/build/pjlib.vcxproj | 4 +- pjlib/build/pjlib.vcxproj.filters | 6 + pjlib/build/pjlib_test.vcproj | 112 ++++ pjlib/build/pjlib_test.vcxproj | 1 + pjlib/build/pjlib_test.vcxproj.filters | 3 + pjlib/include/pj/fifobuf.h | 103 +++- pjlib/include/pj/unittest.h | 734 ++++++++++++++++++++++ pjlib/include/pjlib.h | 1 + pjlib/src/pj/fifobuf.c | 111 ++-- pjlib/src/pj/unittest.c | 824 +++++++++++++++++++++++++ pjlib/src/pjlib-test/fifobuf.c | 200 ++++-- pjlib/src/pjlib-test/test.c | 6 + pjlib/src/pjlib-test/test.h | 4 + pjlib/src/pjlib-test/unittest_test.c | 669 ++++++++++++++++++++ 16 files changed, 2792 insertions(+), 106 deletions(-) create mode 100644 pjlib/include/pj/unittest.h create mode 100644 pjlib/src/pj/unittest.c create mode 100644 pjlib/src/pjlib-test/unittest_test.c diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile index 7d16780435..d13951b042 100644 --- a/pjlib/build/Makefile +++ b/pjlib/build/Makefile @@ -36,7 +36,7 @@ export PJLIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ os_info.o pool.o pool_buf.o pool_caching.o pool_dbg.o rand.o \ rbtree.o sock_common.o sock_qos_common.o \ ssl_sock_common.o ssl_sock_ossl.o ssl_sock_gtls.o ssl_sock_dump.o \ - ssl_sock_darwin.o string.o timer.o types.o + ssl_sock_darwin.o string.o timer.o types.o unittest.o export PJLIB_CFLAGS += $(_CFLAGS) export PJLIB_CXXFLAGS += $(_CXXFLAGS) export PJLIB_LDFLAGS += $(_LDFLAGS) @@ -52,7 +52,7 @@ export TEST_OBJS += activesock.o atomic.o echo_clt.o errno.o exception.o \ select.o sleep.o sock.o sock_perf.o ssl_sock.o \ string.o test.o thread.o timer.o timestamp.o \ udp_echo_srv_sync.o udp_echo_srv_ioqueue.o \ - util.o + unittest_test.o util.o export TEST_CFLAGS += $(_CFLAGS) export TEST_CXXFLAGS += $(_CXXFLAGS) export TEST_LDFLAGS += $(PJLIB_LDLIB) $(_LDFLAGS) diff --git a/pjlib/build/pjlib.vcproj b/pjlib/build/pjlib.vcproj index e3043794c2..afc3441a5c 100644 --- a/pjlib/build/pjlib.vcproj +++ b/pjlib/build/pjlib.vcproj @@ -8420,6 +8420,118 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -12681,6 +12793,10 @@ RelativePath="..\include\pj\unicode.h" > + + diff --git a/pjlib/build/pjlib.vcxproj b/pjlib/build/pjlib.vcxproj index 90ffe174f7..5a58c29aa5 100644 --- a/pjlib/build/pjlib.vcxproj +++ b/pjlib/build/pjlib.vcxproj @@ -1,4 +1,4 @@ - + @@ -1029,6 +1029,7 @@ + @@ -1100,6 +1101,7 @@ + diff --git a/pjlib/build/pjlib.vcxproj.filters b/pjlib/build/pjlib.vcxproj.filters index f4b5682a5d..3a823e52ab 100644 --- a/pjlib/build/pjlib.vcxproj.filters +++ b/pjlib/build/pjlib.vcxproj.filters @@ -173,6 +173,9 @@ Source Files + + Source Files + Source Files\Other Targets @@ -337,6 +340,9 @@ Header Files + + Header Files + Header Files\compat diff --git a/pjlib/build/pjlib_test.vcproj b/pjlib/build/pjlib_test.vcproj index 6876328118..1196a15823 100644 --- a/pjlib/build/pjlib_test.vcproj +++ b/pjlib/build/pjlib_test.vcproj @@ -7334,6 +7334,118 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pjlib/build/pjlib_test.vcxproj b/pjlib/build/pjlib_test.vcxproj index 69d0bef86c..0801881867 100644 --- a/pjlib/build/pjlib_test.vcxproj +++ b/pjlib/build/pjlib_test.vcxproj @@ -795,6 +795,7 @@ + diff --git a/pjlib/build/pjlib_test.vcxproj.filters b/pjlib/build/pjlib_test.vcxproj.filters index 181327a4cc..64451af637 100644 --- a/pjlib/build/pjlib_test.vcxproj.filters +++ b/pjlib/build/pjlib_test.vcxproj.filters @@ -114,6 +114,9 @@ Source Files + + Source Files + Source Files diff --git a/pjlib/include/pj/fifobuf.h b/pjlib/include/pj/fifobuf.h index 26b8fb776e..31190a2c65 100644 --- a/pjlib/include/pj/fifobuf.h +++ b/pjlib/include/pj/fifobuf.h @@ -19,25 +19,108 @@ #ifndef __PJ_FIFOBUF_H__ #define __PJ_FIFOBUF_H__ +/** + * @file fifobuf.h + * @brief Circular buffer + */ +/** + * @defgroup PJ_FIFOBUF Circular buffer + * @ingroup PJ_DS + * @{ + */ + #include PJ_BEGIN_DECL -typedef struct pj_fifobuf_t pj_fifobuf_t; -struct pj_fifobuf_t + +/** + * A FIFO buffer provides chunks of memory to the application with first in + * first out policy (or more correctly, first out first in). The fifobuf is + * created by providing it with a fixed buffer. After that, application may + * request chunks of memory from this buffer. When the app is done with a + * chunk of memory, it must return that chunk back to the fifobuf, with the + * requirement that the oldest allocated chunk must be returned first. + */ +typedef struct pj_fifobuf_t { - char *first, *last; - char *ubegin, *uend; + /** The start of the buffer */ + char *first; + + /** The end of the buffer */ + char *last; + + /** The start of used area in the buffer */ + char *ubegin; + + /** The end of used area in the buffer */ + char *uend; + + /** Full flag when ubegin==uend */ int full; -}; -PJ_DECL(void) pj_fifobuf_init (pj_fifobuf_t *fb, void *buffer, unsigned size); -PJ_DECL(unsigned) pj_fifobuf_max_size (pj_fifobuf_t *fb); -PJ_DECL(void*) pj_fifobuf_alloc (pj_fifobuf_t *fb, unsigned size); -PJ_DECL(pj_status_t) pj_fifobuf_unalloc (pj_fifobuf_t *fb, void *buf); -PJ_DECL(pj_status_t) pj_fifobuf_free (pj_fifobuf_t *fb, void *buf); +} pj_fifobuf_t; + + +/** + * Initialize the fifobuf by giving it a buffer and size. + * + * @param fb The fifobuf + * @param buffer Buffer to be used to allocate/free chunks of memory from by + * the fifo buffer. + * @param size The size of the buffer. + */ +PJ_DECL(void) pj_fifobuf_init(pj_fifobuf_t *fb, void *buffer, unsigned size); + +/** + * Returns the capacity (initial size) of the buffer. + * + * @param fb The fifobuf + * + * @return Capacity in bytes. + */ +PJ_DECL(unsigned) pj_fifobuf_capacity(pj_fifobuf_t *fb); + +/** + * Returns maximum size of memory chunk that can be allocated from the buffer. + * + * @param fb The fifobuf + * + * @return Size in bytes + */ +PJ_DECL(unsigned) pj_fifobuf_available_size(pj_fifobuf_t *fb); + +/** + * Allocate a chunk of memory from the fifobuf. + * + * @param fb The fifobuf + * @param size Size to allocate + * + * @return Allocated buffer or NULL if the buffer cannot be allocated + */ +PJ_DECL(void*) pj_fifobuf_alloc(pj_fifobuf_t *fb, unsigned size); + +/** + * Return the space used by the earliest allocated memory chunk back to the + * fifobuf. For example, if app previously allocated ptr0, ptr1, and ptr2 + * (in that order), then pj_fifobuf_free() can only be called with ptr0 as + * parameter. Subsequent pj_fifobuf_free() must be called with ptr1, and + * the next one with ptr2, and so on. + * + * @param fb The fifobuf + * @param buf Pointer to memory chunk previously returned by + * pj_fifobuf_alloc() + * + * @return PJ_SUCCESS or the appropriate error. + */ +PJ_DECL(pj_status_t) pj_fifobuf_free(pj_fifobuf_t *fb, void *buf); + PJ_END_DECL +/** + * @} // PJ_FIFOBUF group + */ + #endif /* __PJ_FIFOBUF_H__ */ diff --git a/pjlib/include/pj/unittest.h b/pjlib/include/pj/unittest.h new file mode 100644 index 0000000000..c11b252ee5 --- /dev/null +++ b/pjlib/include/pj/unittest.h @@ -0,0 +1,734 @@ +/* + * Copyright (C) 2008-2024 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_UNITTEST_H__ +#define __PJ_UNITTEST_H__ + +/** + * @file testing.h + * @brief PJLIB unit testing framework + */ +/** + * @defgroup PJ_UNITTEST Unit testing framework + * @ingroup PJ_MISC + * @{ + */ +#include +#include +#include +#include + +PJ_BEGIN_DECL + +/* + * These various PJ_TEST_XXX macros can be used in any programs without + * having to use the unit-test framework. + */ + +/** + * Check that an expression is non-zero. If the check fails, informative error + * message will be displayed, and the code in err_action will be executed. + * + * @param expr The expression to check + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_NON_ZERO(expr, err_reason, err_action) \ + { \ + if ((expr)==0) { \ + const char *tmp_reason_ = err_reason; \ + const char *sep0_ = (tmp_reason_ ? " (": ""); \ + const char *sep1_ = (tmp_reason_ ? ")": ""); \ + if (!tmp_reason_) tmp_reason_=""; \ + PJ_LOG(1,(THIS_FILE, "Test \"%s\" != 0 fails in " \ + "%s:%d%s%s%s", \ + #expr, THIS_FILE,__LINE__,sep0_, \ + tmp_reason_,sep1_));\ + err_action; \ + } \ + } + +/** + * Generic check for binary operation. If the check fails, informative error + * message will be displayed, and the code in err_action will be executed. + * + * @param expr0 First expression + * @param op The operator + * @param expr1 Second expression + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_BINARY_OP(expr0, op, expr1, err_reason, err_action) \ + { \ + long tmp_value0_ = (long)(expr0); \ + long tmp_value1_ = (long)(expr1); \ + if (!(tmp_value0_ op tmp_value1_)) { \ + const char *tmp_reason_ = err_reason; \ + const char *sep0_ = (tmp_reason_ ? " (": ""); \ + const char *sep1_ = (tmp_reason_ ? ")": ""); \ + if (!tmp_reason_) tmp_reason_=""; \ + PJ_LOG(1,(THIS_FILE, "Test \"%s\" (value=%ld) " #op \ + " \"%s\" (value=%ld) fails in %s:%d%s%s%s", \ + #expr0, tmp_value0_, #expr1, tmp_value1_, \ + THIS_FILE, __LINE__, \ + sep0_, tmp_reason_, sep1_)); \ + err_action; \ + } \ + } + +/** + * Generic check for (PJ) string comparison operation. If the check fails, + * informative error message will be displayed, and the code in err_action + * will be executed. + * + * @param str_op The string operation (e.g. pj_strcmp) + * @param ps0 Pointer to first string + * @param ps1 Pointer to second string + * @param res_op Operator to compare result (e.g. ==) + * @param res Expected return value of str_op(&s0, &s1) + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_STR_OP(str_op, ps0, ps1, res_op, res, err_reason, err_action) \ + { \ + int result__ = str_op(ps0, ps1); \ + if (!(result__ res_op res)) { \ + const char *fn_name = #str_op; \ + const char *tmp_reason_ = err_reason; \ + const char *sep0_ = (tmp_reason_ ? " (": ""); \ + const char *sep1_ = (tmp_reason_ ? ")": ""); \ + if (!tmp_reason_) tmp_reason_=""; \ + PJ_LOG(1,(THIS_FILE, "Test %s(\"%.*s\", \"%.*s\")%s%d" \ + " fails (%s result=%d) in %s:%d%s%s%s", \ + fn_name, (int)ps0->slen, ps0->ptr, \ + (int)ps1->slen, ps1->ptr, #res_op, res, \ + fn_name, result__, \ + THIS_FILE, __LINE__, \ + sep0_, tmp_reason_, sep1_)); \ + err_action; \ + } \ + } + +/** + * Check that an expression is PJ_SUCCESS. If the check fails, error message + * explaining the error code will be displayed, and the code in err_action + * will be executed. + * + * @param expr The expression to check + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_SUCCESS(expr, err_reason, err_action) \ + { \ + pj_status_t tmp_status_ = (expr); \ + if (tmp_status_ != PJ_SUCCESS) { \ + char errbuf[80]; \ + const char *tmp_reason_ = err_reason; \ + const char *sep0_ = (tmp_reason_ ? " (": ""); \ + const char *sep1_ = (tmp_reason_ ? ")": ""); \ + if (!tmp_reason_) tmp_reason_=""; \ + pj_strerror(tmp_status_, errbuf, sizeof(errbuf)); \ + PJ_LOG(1,(THIS_FILE, "\"%s\" fails in %s:%d, " \ + "status=%d (%s)%s%s%s", \ + #expr, THIS_FILE, __LINE__, tmp_status_,errbuf,\ + sep0_, tmp_reason_, sep1_)); \ + err_action; \ + } \ + } + +/** + * Alias for PJ_TEST_NON_ZERO() + */ +#define PJ_TEST_TRUE(expr, err_reason, err_action) \ + PJ_TEST_NON_ZERO(expr, err_reason, err_action) + +/** + * Alias for PJ_TEST_NON_ZERO() + */ +#define PJ_TEST_NOT_NULL(expr, err_reason, err_action) \ + PJ_TEST_NON_ZERO(expr, err_reason, err_action) + +/** + * Check that expr0 equals expr1. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. + * + * @param expr0 First expression + * @param expr1 Second expression + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_EQ(expr0, expr1, err_reason, err_action) \ + PJ_TEST_BINARY_OP(expr0, ==, expr1, err_reason, err_action) + +/** + * Check that expr0 does not equal expr1. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. + * + * @param expr0 First expression + * @param expr1 Second expression + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_NEQ(expr0, expr1, err_reason, err_action) \ + PJ_TEST_BINARY_OP(expr0, !=, expr1, err_reason, err_action) + +/** + * Check that expr0 is less than expr1. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. + * + * @param expr0 First expression + * @param expr1 Second expression + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_LT(expr0, expr1, err_reason, err_action) \ + PJ_TEST_BINARY_OP(expr0, <, expr1, err_reason, err_action) + +/** + * Check that expr0 is less than or equal to expr1. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. + * + * @param expr0 First expression + * @param expr1 Second expression + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_LTE(expr0, expr1, err_reason, err_action) \ + PJ_TEST_BINARY_OP(expr0, <=, expr1, err_reason, err_action) + +/** + * Check that expr0 is greater than expr1. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. + * + * @param expr0 First expression + * @param expr1 Second expression + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_GT(expr0, expr1, err_reason, err_action) \ + PJ_TEST_BINARY_OP(expr0, >, expr1, err_reason, err_action) + +/** + * Check that expr0 is greater than or equal to expr1. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. + * + * @param expr0 First expression + * @param expr1 Second expression + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_GTE(expr0, expr1, err_reason, err_action) \ + PJ_TEST_BINARY_OP(expr0, >=, expr1, err_reason, err_action) + + +/** + * Check string comparison result. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. + * + * @param ps0 Pointer to first string + * @param ps1 Pointer to second string + * @param res_op Operator to compare result (e.g. ==, <, >) + * @param exp_result Expected result (e.g. zero for equal string) + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_STRCMP(ps0, ps1, res_op, exp_result, err_reason, err_action) \ + PJ_TEST_STR_OP(pj_strcmp, ps0, ps1, res_op, exp_result, \ + err_reason, err_action) + +/** + * Check case-insensitive string comparison result. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. + * + * @param ps0 Pointer to first string + * @param ps1 Pointer to second string + * @param res_op Operator to compare result (e.g. ==, <, >) + * @param exp_result Expected result (e.g. zero for equal) + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_STRICMP(ps0, ps1, res_op, exp_result, err_reason, err_action) \ + PJ_TEST_STR_OP(pj_stricmp, ps0, ps1, res_op, exp_result, \ + err_reason, err_action) + +/** + * Check that two strings are equal. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. + * + * @param ps0 Pointer to first string + * @param ps1 Pointer to second string + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_STREQ(ps0, ps1, err_reason, err_action) \ + PJ_TEST_STRCMP(ps0, ps1, ==, 0, err_reason, err_action) + +/** + * Check that two strings are not equal. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. + * + * @param ps0 Pointer to first string + * @param ps1 Pointer to second string + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_STRNEQ(ps0, ps1, err_reason, err_action) \ + PJ_TEST_STRCMP(ps0, ps1, !=, 0, err_reason, err_action) + + +/** + * Bitwise constants that can be used in test case flags (see + * pj_test_case_init()). + */ +typedef enum pj_test_case_flag +{ + /** + * Do not allow other test cases to run while this test case is running. + * Note this only makes sense for test runners that support worker + * threads. Basic runner will always run test cases serially. + */ + PJ_TEST_EXCLUSIVE = 1, + + /** + * Specify that the test function must be called without argument. + * This is mainly for backward compatibility with existing PJ test + * functions which take no argument. + */ + PJ_TEST_FUNC_NO_ARG = 2, + + /** + * Write the original log at the time it is called instead of pooling + * the logs to be printed after all tests finish. + */ + PJ_TEST_LOG_NO_CACHE = 4, + + /** + * Keep this test case in front of the list when shuffling the test + * cases. + */ + PJ_TEST_KEEP_FIRST = 8, + + /** + * Keep this test case in last in the list when shuffling the test + * cases. + */ + PJ_TEST_KEEP_LAST = 16, + +} pj_test_case_flag; + + +/** + * An internal structure to represent one logging item that is saved + * inside pj_test_case. + */ +typedef struct pj_test_log_item +{ + PJ_DECL_LIST_MEMBER(struct pj_test_log_item); + + /** level */ + int level; + + /** len */ + int len; + + /** The log message. The actual buffer is longer. */ + char msg[1]; + +} pj_test_log_item; + + +/** Forward declaration of test runner */ +typedef struct pj_test_runner pj_test_runner; + + +/** + * Additional parameters for creating test case. Use + * pj_test_case_param_default() to initialize this structure. + */ +typedef struct pj_test_case_param +{ + /** + * Custom log level for this test case, to filter out logs that are more + * detail than this level. Default is 6, meaning it will accept all log + * levels. + */ + int log_level; + +} pj_test_case_param; + + +/** + * A test case is unit-test object to perform test against a user defined + * function. + */ +typedef struct pj_test_case +{ + PJ_DECL_LIST_MEMBER(struct pj_test_case); + + /** Test name */ + char obj_name[PJ_MAX_OBJ_NAME]; + + /** + * The test function to be called to perform the test. By convention, the + * function must return zero for the test to be considered successful, + * non-zero on failure, and MUST NEVER return PJ_EPENDING, otherwise the + * return value will be silently changed to -12345. + */ + int (*test_func)(void*); + + /** Argument to be passed to the test function */ + void *arg; + + /** Flags, combination of pj_test_flag constants */ + unsigned flags; + + /** Circular buffer for logging */ + pj_fifobuf_t fb; + + /** Parameters */ + pj_test_case_param prm; + + /** + * The return value of the test function. Zero indicates success. Initially + * the value is PJ_EPENDING before the test is run. + */ + int result; + + /** List of saved logging messages */ + pj_test_log_item logs; + + /** Pointer to the runner running this test case */ + pj_test_runner *runner; + + /** Start time */ + pj_timestamp start_time; + + /** End time */ + pj_timestamp end_time; + +} pj_test_case; + + +/** + * Test suite is a collection of test cases. + */ +typedef struct pj_test_suite +{ + /** List of tests */ + pj_test_case tests; + + /** Start time */ + pj_timestamp start_time; + + /** End time */ + pj_timestamp end_time; + +} pj_test_suite; + + +/** + * Test statistics. Collect the statistics after the test runner finishes + * with pj_test_get_stat(). + */ +typedef struct pj_test_stat +{ + /** Total duration */ + pj_time_val duration; + + /** Total number of tests in the test suite */ + unsigned ntests; + + /** Number of tests run */ + unsigned nruns; + + /** Number of failed tests */ + unsigned nfailed; + + /** + * Array of failed test names. Be careful that the number elements are + * fixed, hence it may not be able to store all failed test names (in case + * nfailed is more than the capacity, not all failed test names will be + * stored, hence be careful in the loop). + */ + const char *failed_names[32]; + +} pj_test_stat; + + +/** + * Test runner parameters. Use pj_test_runner_param_default() to initialize + * this structure. + */ +typedef struct pj_test_runner_param +{ + /** Stop the test on error (default: false) */ + pj_bool_t stop_on_error; + + /** Number of worker threads. Set to zero to disable parallel testings. + * Only applicable to test text runner. Default is 1 if multithreading + * is available. + */ + unsigned nthreads; + + /** + * 0: only display test name and result after test completion (default) + * 1: display test name test when starting and finishing a test + */ + unsigned verbosity; + +} pj_test_runner_param; + + +/** + * This structure represents a test runner. Currently there are two types + * of test runners, the basic runner and text runner. The basic runner is the + * simplest test runner that can be used without pool and threads, and can be + * created with pj_test_init_basic_runner(). The text runner is more powerful + * since it supports worker threads, and it is mostly suitable for console + * based environments. It is created with pj_test_create_text_runner(). + */ +struct pj_test_runner +{ + /** Parameters */ + pj_test_runner_param prm; + + /** The test suite being run */ + pj_test_suite *suite; + + /** Saving the original log writer */ + pj_log_func *orig_log_writer; + + /** Number of tests */ + unsigned ntests; + + /** Number of completed tests */ + unsigned nruns; + + /** Stopping */ + pj_bool_t stopping; + + /** main method */ + void (*main)(pj_test_runner*); + + /** callback when test case completes. Default is to write to log */ + void (*on_test_complete)(pj_test_runner*, pj_test_case*); + + /** destroy method */ + void (*destroy)(pj_test_runner*); +}; + +/** Option to select tests (e.g. in pj_test_dump_log_messages()) */ +typedef enum pj_test_select_tests +{ + /** Select no test*/ + PJ_TEST_NO_TEST = 0, + + /** Select only failed tests */ + PJ_TEST_FAILED_TESTS = 1, + + /** Select only successful tests */ + PJ_TEST_SUCCESSFUL_TESTS = 2, + + /** Select all tests*/ + PJ_TEST_ALL_TESTS = 3, + + /** No header/footer separator */ + PJ_TEST_NO_HEADER_FOOTER = 4, + +} pj_test_select_tests; + + +/** + * Initialize test suite. + * + * @param suite The test suite + */ +PJ_DECL(void) pj_test_suite_init(pj_test_suite *suite); + +/** + * Initialize pj_test_case_param with default values. If app only uses + * default values in params, alternatively it doesn't need to use param + * at all and just specify NULL in pj_test_case_init(). + * + * @param prm The parameter. + */ +PJ_DECL(void) pj_test_case_param_default(pj_test_case_param *prm); + +/** + * Initialize test case. + * + * @param tc The test case + * @param obj_name Name that will appear as test name/title + * @param flags Bitwise of pj_test_case_flag to control threading, + * function calling, logging, etc. + * @param test_func The test function to be called to perform the test. + * By convention, the function must return zero for the + * test to be considered successful, non-zero on failure, + * and MUST NEVER return PJ_EPENDING, otherwise the + * return value will be silently changed to -12345. + * @param arg Argument to give to the test function + * @param fifobuf_buf Buffer for saving the logs, if required. + * @param buf_size Size of the buffer for saving the logs. + * @param prm Optional additional settings for the test case or NULL + */ +PJ_DECL(void) pj_test_case_init(pj_test_case *tc, + const char *obj_name, + unsigned flags, + int (*test_func)(void*), + void *arg, + void *fifobuf_buf, + unsigned buf_size, + const pj_test_case_param *prm); + +/** + * Add test case to test suite. A test case can only be added to one suite. + * + * @param suite The test suite + * @param tc The test case + */ +PJ_DECL(void) pj_test_suite_add_case(pj_test_suite *suite, pj_test_case *tc); + +/** + * Shuffle the tests. + * + * @param suite The test suite + * @param seed Optional random seed to use, only if the value is + * greater than or equal to zero. It is recommended + * to set this value to make the test reproducible. + */ +PJ_DECL(void) pj_test_suite_shuffle(pj_test_suite *suite, int seed); + +/** + * Initialize parameters with reasonable default values. This usually means + * using one worker thread if threading is enabled, and zero worker thread + * (i.e. only use the main thread) otherwise. + * + * @param prm Test runner parameter + */ +PJ_DECL(void) pj_test_runner_param_default(pj_test_runner_param *prm); + +/** + * Initialize a basic test runner. A basic runner can be declared in the stack + * and it does not require pool nor multithreading. + * + * @param runner The runner. + * @param prm Runner params, or NULL to accept default values. + */ +PJ_DECL(void) pj_test_init_basic_runner(pj_test_runner *runner, + const pj_test_runner_param *prm); + +/** + * Create console based test runner. + * + * @param pool The pool to use to allocate memory + * @param prm Test runner parameter, or NULL for default values. + * @param p_runner Pointer to receive the text runner + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_test_create_text_runner( + pj_pool_t *pool, + const pj_test_runner_param *prm, + pj_test_runner **p_runner); + +/** + * Run test suite with the specified runner. + * + * @param runner The test runner + * @param suite The test suite + */ +PJ_DECL(void) pj_test_run(pj_test_runner *runner, + pj_test_suite *suite); + +/** + * This is a crude test to detect if thread is currently running under + * a test. It is mainly used to prevent nested unit testing. + * + * @return PJ_TRUE if we are currently running in the context of a test case + * being run. + */ +PJ_DECL(pj_bool_t) pj_test_is_under_test(void); + +/** + * Get the test statistics after the run completes. The test suite and + * test cases instances must be kept alive in order to get and access the + * statistics or log messages. + * + * @param suite The test suite + * @param stat The test statistics result. + */ +PJ_DECL(void) pj_test_get_stat(const pj_test_suite *suite, pj_test_stat *stat); + +/** + * Display statistics to the log. + * + * @param stat The test statistics result. + */ +PJ_DECL(void) pj_test_display_stat(const pj_test_stat *stat, + const char *test_name, + const char *log_sender); + +/** + * Display previously saved log messages in the test cases to logging. + * Note that log messages emited during test case's run are only saved + * when fifobuf of the test case is configured with a suitable buffer. + * Also note that the test suite and test cases instances must be kept alive + * in order to get and access the statistics or log messages. + * + * @param suite The test suite + * @param flags Select which test logs to display by choosing + * from pj_test_select_tests. + */ +PJ_DECL(void) pj_test_display_log_messages(const pj_test_suite *suite, + unsigned flags); + +/** + * Destroy the runner. Runner may be destroyed right after it is run, + * but the test suite and test cases instances must be kept alive in order + * to get the statistics or log messages. + * + * @param runner The test runner. + */ +PJ_DECL(void) pj_test_runner_destroy(pj_test_runner *runner); + + +/** + * Macro to control how long worker thread should sleep waiting for next + * ready test. + */ +#ifndef PJ_TEST_THREAD_WAIT_MSEC +# define PJ_TEST_THREAD_WAIT_MSEC 100 +#endif + +PJ_END_DECL + +/** + * @} // PJ_UNITTEST group + */ + +#endif /* __PJ_UNITTEST_H__ */ + diff --git a/pjlib/include/pjlib.h b/pjlib/include/pjlib.h index dfb9e53f24..1bc591be51 100644 --- a/pjlib/include/pjlib.h +++ b/pjlib/include/pjlib.h @@ -55,6 +55,7 @@ #include #include #include +#include #include diff --git a/pjlib/src/pj/fifobuf.c b/pjlib/src/pj/fifobuf.c index 464f3114cc..53e5787c10 100644 --- a/pjlib/src/pj/fifobuf.c +++ b/pjlib/src/pj/fifobuf.c @@ -17,14 +17,29 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #include #include #include +#include #define THIS_FILE "fifobuf" #define SZ sizeof(unsigned) +/* put and get size at arbitrary, possibly unaligned location */ +PJ_INLINE(void) put_size(void *ptr, unsigned size) +{ + pj_memcpy(ptr, &size, sizeof(size)); +} + +PJ_INLINE(unsigned) get_size(const void *ptr) +{ + unsigned size; + pj_memcpy(&size, ptr, sizeof(size)); + return size; +} + PJ_DEF(void) pj_fifobuf_init (pj_fifobuf_t *fifobuf, void *buffer, unsigned size) { PJ_CHECK_STACK(); @@ -36,23 +51,38 @@ PJ_DEF(void) pj_fifobuf_init (pj_fifobuf_t *fifobuf, void *buffer, unsigned size fifobuf->first = (char*)buffer; fifobuf->last = fifobuf->first + size; fifobuf->ubegin = fifobuf->uend = fifobuf->first; - fifobuf->full = 0; + fifobuf->full = (fifobuf->last==fifobuf->first); } -PJ_DEF(unsigned) pj_fifobuf_max_size (pj_fifobuf_t *fifobuf) +PJ_DEF(unsigned) pj_fifobuf_capacity (pj_fifobuf_t *fifobuf) { - unsigned s1, s2; + unsigned cap = (unsigned)(fifobuf->last - fifobuf->first); + return (cap > 0) ? cap-SZ : 0; +} +PJ_DEF(unsigned) pj_fifobuf_available_size (pj_fifobuf_t *fifobuf) +{ PJ_CHECK_STACK(); + if (fifobuf->full) + return 0; + if (fifobuf->uend >= fifobuf->ubegin) { - s1 = (unsigned)(fifobuf->last - fifobuf->uend); - s2 = (unsigned)(fifobuf->ubegin - fifobuf->first); + unsigned s; + unsigned s1 = (unsigned)(fifobuf->last - fifobuf->uend); + unsigned s2 = (unsigned)(fifobuf->ubegin - fifobuf->first); + if (s1 <= SZ) + s = s2; + else if (s2 <= SZ) + s = s1; + else + s = s1=SZ) ? s-SZ : 0; } else { - s1 = s2 = (unsigned)(fifobuf->ubegin - fifobuf->uend); + unsigned s = (unsigned)(fifobuf->ubegin - fifobuf->uend); + return (s>=SZ) ? s-SZ : 0; } - - return s1uend >= fifobuf->ubegin) { + /* If we got here, then first <= ubegin <= uend <= last, and + * the the buffer layout is like this: + * + * <--free0---> <--- used --> <-free1-> + * | |#############| | + * ^ ^ ^ ^ + * first ubegin uend last + * + * where the size of free0, used, and/or free1 may be zero. + */ available = (unsigned)(fifobuf->last - fifobuf->uend); if (available >= size+SZ) { char *ptr = fifobuf->uend; @@ -79,17 +119,26 @@ PJ_DEF(void*) pj_fifobuf_alloc (pj_fifobuf_t *fifobuf, unsigned size) fifobuf->uend = fifobuf->first; if (fifobuf->uend == fifobuf->ubegin) fifobuf->full = 1; - *(unsigned*)ptr = size+SZ; + put_size(ptr, size+SZ); ptr += SZ; PJ_LOG(6, (THIS_FILE, - "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p", + "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p", fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend)); return ptr; } } - /* try to allocate from the start part of the fifo */ + /* If we got here, then either there is not enough space in free1 above, + * or the the buffer layout is like this: + * + * <-used0-> <--free--> <- used1 -> + * |#########| |###########| + * ^ ^ ^ ^ + * first uend ubegin last + * + * where the size of used0, used1, and/or free may be zero. + */ start = (fifobuf->uend <= fifobuf->ubegin) ? fifobuf->uend : fifobuf->first; available = (unsigned)(fifobuf->ubegin - start); if (available >= size+SZ) { @@ -97,7 +146,7 @@ PJ_DEF(void*) pj_fifobuf_alloc (pj_fifobuf_t *fifobuf, unsigned size) fifobuf->uend = start + size + SZ; if (fifobuf->uend == fifobuf->ubegin) fifobuf->full = 1; - *(unsigned*)ptr = size+SZ; + put_size(ptr, size+SZ); ptr += SZ; PJ_LOG(6, (THIS_FILE, @@ -112,36 +161,6 @@ PJ_DEF(void*) pj_fifobuf_alloc (pj_fifobuf_t *fifobuf, unsigned size) return NULL; } -PJ_DEF(pj_status_t) pj_fifobuf_unalloc (pj_fifobuf_t *fifobuf, void *buf) -{ - char *ptr = (char*)buf; - char *endptr; - unsigned sz; - - PJ_CHECK_STACK(); - - ptr -= SZ; - sz = *(unsigned*)ptr; - - endptr = fifobuf->uend; - if (endptr == fifobuf->first) - endptr = fifobuf->last; - - if (ptr+sz != endptr) { - pj_assert(!"Invalid pointer to undo alloc"); - return -1; - } - - fifobuf->uend = ptr; - fifobuf->full = 0; - - PJ_LOG(6, (THIS_FILE, - "fifobuf_unalloc fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p", - fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend)); - - return 0; -} - PJ_DEF(pj_status_t) pj_fifobuf_free (pj_fifobuf_t *fifobuf, void *buf) { char *ptr = (char*)buf; @@ -153,19 +172,19 @@ PJ_DEF(pj_status_t) pj_fifobuf_free (pj_fifobuf_t *fifobuf, void *buf) ptr -= SZ; if (ptr < fifobuf->first || ptr >= fifobuf->last) { pj_assert(!"Invalid pointer to free"); - return -1; + return PJ_EINVAL; } if (ptr != fifobuf->ubegin && ptr != fifobuf->first) { pj_assert(!"Invalid free() sequence!"); - return -1; + return PJ_EINVAL; } end = (fifobuf->uend > fifobuf->ubegin) ? fifobuf->uend : fifobuf->last; - sz = *(unsigned*)ptr; + sz = get_size(ptr); if (ptr+sz > end) { pj_assert(!"Invalid size!"); - return -1; + return PJ_EINVAL; } fifobuf->ubegin = ptr + sz; diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c new file mode 100644 index 0000000000..d8ac75132d --- /dev/null +++ b/pjlib/src/pj/unittest.c @@ -0,0 +1,824 @@ +/* + * Copyright (C) 2008-2024 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include + +#define THIS_FILE "unittest.c" +#define INVALID_TLS_ID -1 + +static long tls_id = INVALID_TLS_ID; + +/* When basic runner is used, current test is saved in this global var */ +static pj_test_case *tc_main_thread; + +/* Forward decls. */ +static void unittest_log_callback(int level, const char *data, int len); +static int get_completion_line( const pj_test_case *tc, const char *end_line, + char *log_buf, unsigned buf_size); + +/* atexit() callback to free TLS */ +static void unittest_shutdown(void) +{ + if (tls_id != INVALID_TLS_ID) { + pj_thread_local_free(tls_id); + tls_id = INVALID_TLS_ID; + } +} + +/* initialize unittest subsystem. can be called many times. */ +static pj_status_t unittest_init(void) +{ +#if PJ_HAS_THREADS + if (tls_id == INVALID_TLS_ID) { + pj_status_t status; + status = pj_thread_local_alloc(&tls_id); + if (status != PJ_SUCCESS) { + tls_id = INVALID_TLS_ID; + return status; + } + + pj_atexit(&unittest_shutdown); + } +#endif + return PJ_SUCCESS; +} + +/* Initialize param with default values */ +PJ_DEF(void) pj_test_case_param_default( pj_test_case_param *prm) +{ + pj_bzero(prm, sizeof(*prm)); + prm->log_level = 6; +} + +/* Initialize test case */ +PJ_DEF(void) pj_test_case_init( pj_test_case *tc, + const char *obj_name, + unsigned flags, + int (*test_func)(void*), + void *arg, + void *fifobuf_buf, + unsigned buf_size, + const pj_test_case_param *prm) +{ + PJ_ASSERT_ON_FAIL( ((flags & (PJ_TEST_KEEP_FIRST|PJ_TEST_KEEP_LAST)) != + (PJ_TEST_KEEP_FIRST|PJ_TEST_KEEP_LAST)), {} ); + pj_bzero(tc, sizeof(*tc)); + + /* Parameters */ + if (prm) { + pj_memcpy(&tc->prm, prm, sizeof(*prm)); + } else { + pj_test_case_param_default(&tc->prm); + } + pj_ansi_strxcpy(tc->obj_name, obj_name, sizeof(tc->obj_name)); + tc->flags = flags; + tc->test_func = test_func; + tc->arg = arg; + pj_fifobuf_init(&tc->fb, fifobuf_buf, buf_size); + + /* Run-time state */ + tc->result = PJ_EPENDING; + pj_list_init(&tc->logs); +} + +/* Init test suite */ +PJ_DEF(void) pj_test_suite_init(pj_test_suite *suite) +{ + pj_bzero(suite, sizeof(*suite)); + pj_list_init(&suite->tests); +} + +/* Add test case */ +PJ_DEF(void) pj_test_suite_add_case(pj_test_suite *suite, pj_test_case *tc) +{ + pj_list_push_back(&suite->tests, tc); +} + +/* Shuffle */ +PJ_DEF(void) pj_test_suite_shuffle(pj_test_suite *suite, int seed) +{ + pj_test_case src, *tc; + unsigned total, movable; + + if (seed >= 0) + pj_srand(seed); + + /* Move tests to new list */ + pj_list_init(&src); + pj_list_merge_last(&src, &suite->tests); + + /* Move KEEP_FIRST tests first */ + for (tc=src.next; tc!=&src; ) { + pj_test_case *next = tc->next; + if (tc->flags & PJ_TEST_KEEP_FIRST) { + pj_list_erase(tc); + pj_list_push_back(&suite->tests, tc); + } + + tc = next; + } + + /* Count non-KEEP_LAST tests */ + for (total=0, movable=0, tc=src.next; tc!=&src; tc=tc->next) { + ++total; + if ((tc->flags & PJ_TEST_KEEP_LAST)==0) + ++movable; + } + + /* Shuffle non KEEP_LAST tests */ + while (movable > 0) { + int step = pj_rand() % total; + if (step < 0) + continue; + + for (tc=src.next; step>0; tc=tc->next, --step) + ; + + pj_assert(tc!=&src); + if (tc->flags & PJ_TEST_KEEP_LAST) + continue; + + pj_list_erase(tc); + pj_list_push_back(&suite->tests, tc); + --movable; + --total; + } + + /* Move KEEP_LAST tests */ + for (tc=src.next; tc!=&src; ) { + pj_test_case *next = tc->next; + pj_assert(tc->flags & PJ_TEST_KEEP_LAST); + pj_list_erase(tc); + pj_list_push_back(&suite->tests, tc); + tc = next; + } +} + +/* Initialize text runner param with default values */ +PJ_DEF(void) pj_test_runner_param_default(pj_test_runner_param *prm) +{ + pj_bzero(prm, sizeof(*prm)); +#if PJ_HAS_THREADS + prm->nthreads = 1; +#endif +} + +/* Main API to start running a test runner */ +PJ_DEF(void) pj_test_run(pj_test_runner *runner, pj_test_suite *suite) +{ + pj_test_case *tc; + + /* Redirect logging to our custom callback */ + runner->orig_log_writer = pj_log_get_log_func(); + pj_log_set_log_func(&unittest_log_callback); + + /* Initialize suite and test cases */ + runner->suite = suite; + runner->ntests = (unsigned)pj_list_size(&suite->tests); + runner->nruns = 0; + + for (tc=suite->tests.next; tc!=&suite->tests; + tc=tc->next) + { + tc->result = PJ_EPENDING; + tc->runner = NULL; /* WIll be assigned runner when is run */ + } + + /* Call the run method to perform runner specific loop */ + pj_get_timestamp(&suite->start_time); + runner->main(runner); + pj_get_timestamp(&suite->end_time); + + /* Restore logging */ + pj_log_set_log_func(runner->orig_log_writer); +} + +/* Check if we are under test */ +PJ_DEF(pj_bool_t) pj_test_is_under_test(void) +{ + return pj_log_get_log_func()==&unittest_log_callback; +} + +/* Calculate statistics */ +PJ_DEF(void) pj_test_get_stat( const pj_test_suite *suite, pj_test_stat *stat) +{ + const pj_test_case *tc; + + pj_bzero(stat, sizeof(*stat)); + stat->duration = pj_elapsed_time(&suite->start_time, &suite->end_time); + stat->ntests = (unsigned)pj_list_size(&suite->tests); + + for (tc=suite->tests.next; tc!=&suite->tests; tc=tc->next) { + if (tc->result != PJ_EPENDING) { + stat->nruns++; + if (tc->result != PJ_SUCCESS) { + if (stat->nfailed < PJ_ARRAY_SIZE(stat->failed_names)) { + stat->failed_names[stat->nfailed] = tc->obj_name; + } + stat->nfailed++; + } + } + } +} + +/* Display statistics */ +PJ_DEF(void) pj_test_display_stat(const pj_test_stat *stat, + const char *test_name, + const char *log_sender) +{ + PJ_LOG(3,(log_sender, "Unit test statistics for %s:", test_name)); + PJ_LOG(3,(log_sender, " Total number of tests: %d", stat->ntests)); + PJ_LOG(3,(log_sender, " Number of test run: %d", stat->nruns)); + PJ_LOG(3,(log_sender, " Number of failed test: %d", stat->nfailed)); + PJ_LOG(3,(log_sender, " Total duration: %dm%d.%03ds", + (int)stat->duration.sec/60, (int)stat->duration.sec%60, + (int)stat->duration.msec)); +} + +/* Get name with argument info if any, e.g. "ice_test (arg: 1)" */ +static const char *get_test_case_info(const pj_test_case *tc, + char *buf, unsigned size) +{ + char arg_info[64]; + if (tc->flags & PJ_TEST_FUNC_NO_ARG) { + arg_info[0] = '\0'; + } else { + char arg_val[40]; + /* treat argument as integer */ + pj_ansi_snprintf(arg_val, sizeof(arg_val), "%ld", (long)tc->arg); + + /* if arg value is too long (e.g. it's a pointer!), then just show + * a portion of it */ + if (pj_ansi_strlen(arg_val) > 6) { + pj_ansi_strxcat(arg_val+6, "...", sizeof(arg_val)); + } + + pj_ansi_snprintf(arg_info, sizeof(arg_info), " (arg: %s)", arg_val); + } + pj_ansi_snprintf(buf, size, "%s%s", tc->obj_name, arg_info); + return buf; +} + +/* Dump previously saved log messages */ +PJ_DEF(void) pj_test_display_log_messages(const pj_test_suite *suite, + unsigned flags) +{ + const pj_test_case *tc = suite->tests.next; + pj_log_func *log_writer = pj_log_get_log_func(); + char tcname[64]; + const char *title; + + if ((flags & PJ_TEST_ALL_TESTS)==PJ_TEST_ALL_TESTS) + title = "all"; + else if ((flags & PJ_TEST_ALL_TESTS)==PJ_TEST_FAILED_TESTS) + title = "failed"; + else if ((flags & PJ_TEST_ALL_TESTS)==PJ_TEST_SUCCESSFUL_TESTS) + title = "successful"; + else + title = "unknown"; + + while (tc != &suite->tests) { + const pj_test_log_item *log_item = tc->logs.next; + + if ((tc->result == PJ_EPENDING) || + ((flags & PJ_TEST_ALL_TESTS)==PJ_TEST_FAILED_TESTS && + tc->result==0) || + ((flags & PJ_TEST_ALL_TESTS)==PJ_TEST_SUCCESSFUL_TESTS && + tc->result!=0)) + { + /* Test doesn't meet criteria */ + tc = tc->next; + continue; + } + + if (log_item != &tc->logs) { + if (title && (flags & PJ_TEST_NO_HEADER_FOOTER)==0) { + PJ_LOG(3,(THIS_FILE, + "------------ Displaying %s test logs: ------------", + title)); + title = NULL; + } + + PJ_LOG(3,(THIS_FILE, "------------ Logs for %s [rc:%d]: ------------", + get_test_case_info(tc, tcname, sizeof(tcname)), + tc->result)); + + do { + log_writer(log_item->level, log_item->msg, log_item->len); + log_item = log_item->next; + } while (log_item != &tc->logs); + } + tc = tc->next; + } + + if (!title) { + PJ_LOG(3,(THIS_FILE, + "--------------------------------------------------------")); + } +} + +/* Destroy runner */ +PJ_DEF(void) pj_test_runner_destroy(pj_test_runner *runner) +{ + runner->destroy(runner); +} + +/**************************** Common for runners ****************************/ + +/* Set the current test case being run by a thread. The logging callback + * needs this info. + */ +static void set_current_test_case(pj_test_case *tc) +{ + if (tls_id == INVALID_TLS_ID) + tc_main_thread = tc; + else + pj_thread_local_set(tls_id, tc); +} + + +/* Get the current test case being run by a thread. The logging callback + * needs this info. + */ +static pj_test_case *get_current_test_case() +{ + if (tls_id == INVALID_TLS_ID) + return tc_main_thread; + else + return (pj_test_case*) pj_thread_local_get(tls_id); +} + +/* Logging callback */ +static void unittest_log_callback(int level, const char *data, int len) +{ + pj_test_case *tc = get_current_test_case(); + unsigned req_size, free_size; + pj_bool_t truncated; + pj_test_log_item *log_item; + + if (len < 1) + return; + + if (tc==NULL) { + /* We are being called by thread that is not part of unit-test. + * Call the original log writer, hoping that the thread did not + * change the writer before this.. (note: this can only be solved + * by setting pj_log_set/get_log_func() to be thread specific.) + */ + pj_log_write(level, data, len); + return; + } + + /* Filter out unwanted log */ + if (level > tc->prm.log_level) + return; + + /* If the test case wants to display the original log as they are called, + * then write it using the original logging writer now. + */ + if (tc->flags & PJ_TEST_LOG_NO_CACHE) { + tc->runner->orig_log_writer(level, data, len); + return; + } + + /* If fifobuf is not configured on this test case, there's nothing + * we can do. We assume tester doesn't want logging. + */ + if (pj_fifobuf_capacity(&tc->fb)==0) + return; + + /* Required size is the message length plus sizeof(pj_test_log_item). + * This should be enough to save the message INCLUDING the null + * character (because of msg[1] in pj_test_log_item) + */ + req_size = len + sizeof(pj_test_log_item); + + /* Free the buffer until it's enough to save the message. */ + while ((free_size = pj_fifobuf_available_size(&tc->fb)) < req_size && + !pj_list_empty(&tc->logs)) + { + pj_test_log_item *first = tc->logs.next; + + /* Free the oldest */ + pj_list_erase(first); + pj_fifobuf_free(&tc->fb, first); + } + + if (free_size < sizeof(pj_test_log_item) + 10) { + /* Tester has set the fifobuf's size too small */ + return; + } + + if (free_size < req_size) { + /* Truncate message */ + len = free_size - sizeof(pj_test_log_item); + req_size = free_size; + truncated = PJ_TRUE; + } else { + truncated = PJ_FALSE; + } + + log_item = (pj_test_log_item*)pj_fifobuf_alloc(&tc->fb, req_size); + PJ_ASSERT_ON_FAIL(log_item, return); + log_item->level = level; + log_item->len = len; + pj_memcpy(log_item->msg, data, len+1); + if (truncated) + log_item->msg[len-1] = '\n'; + pj_list_push_back(&tc->logs, log_item); +} + +/* Create test case completion line, i.e. the one that looks like: + * [2/24] pool_test [OK] + */ +static int get_completion_line( const pj_test_case *tc, const char *end_line, + char *log_buf, unsigned buf_size) +{ + char tcname[64]; + char res_buf[64]; + pj_time_val elapsed; + int log_len; + + elapsed = pj_elapsed_time(&tc->start_time, &tc->end_time); + + if (tc->result==0) { + pj_ansi_snprintf(res_buf, sizeof(res_buf), "[OK] [%d.%03ds]", + (int)elapsed.sec, (int)elapsed.msec); + } else if (tc->result==PJ_EPENDING) { + pj_ansi_strxcpy(res_buf, "pending", sizeof(res_buf)); + } else { + pj_ansi_snprintf(res_buf, sizeof(res_buf), "[Err: %d] [%d.%03ds]", + tc->result, (int)elapsed.sec, (int)elapsed.msec); + } + + log_len = pj_ansi_snprintf(log_buf, buf_size, "%-32s %s%s\n", + get_test_case_info(tc, tcname, sizeof(tcname)), + res_buf, end_line); + + if (log_len < 1 || log_len >= sizeof(log_buf)) + log_len = (int)pj_ansi_strlen(log_buf); + + return log_len; +} + +/* This is the main function to run a single test case. It may + * be used by the basic runner, which has no threads (=no TLS), + * no fifobuf, no pool, or by multiple threads. + */ +static void run_test_case(pj_test_runner *runner, int tid, pj_test_case *tc) +{ + char tcname[64]; + + if (runner->prm.verbosity >= 1) { + const char *exclusivity = (tc->flags & PJ_TEST_EXCLUSIVE) ? + " (exclusive)" : ""; + get_test_case_info(tc, tcname, sizeof(tcname)); + PJ_LOG(3,(THIS_FILE, "Thread %d starts running %s%s", + tid, tcname, exclusivity)); + } + + /* Set the test case being worked on by this thread */ + set_current_test_case(tc); + + tc->runner = runner; + pj_get_timestamp(&tc->start_time); + + /* Call the test case's function */ + if (tc->flags & PJ_TEST_FUNC_NO_ARG) { + /* Function without argument */ + typedef int (*func_t)(void); + func_t func = (func_t)tc->test_func; + tc->result = func(); + } else { + tc->result = tc->test_func(tc->arg); + } + + if (tc->result == PJ_EPENDING) + tc->result = -12345; + + if (tc->result && runner->prm.stop_on_error) + runner->stopping = PJ_TRUE; + + pj_get_timestamp(&tc->end_time); + runner->on_test_complete(runner, tc); + + /* Reset the test case being worked on by this thread */ + set_current_test_case(NULL); + + if (runner->prm.verbosity >= 1) { + PJ_LOG(3,(THIS_FILE, "Thread %d done running %s (rc: %d)", + tid, tcname, tc->result)); + } +} + +static pj_test_case *get_first_running(pj_test_case *tests) +{ + pj_test_case *tc; + for (tc=tests->next; tc!=tests; tc=tc->next) { + if (tc->runner && tc->result==PJ_EPENDING) + return tc; + } + return NULL; +} + +static pj_test_case *get_next_to_run(pj_test_case *tests) +{ + pj_test_case *tc; + for (tc=tests->next; tc!=tests; tc=tc->next) { + if (tc->runner==NULL) { + assert(tc->result==PJ_EPENDING); + return tc; + } + } + return NULL; +} + +/******************************* Basic Runner *******************************/ + +/* This is the "main()" function for basic runner. It just runs the tests + * sequentially + */ +static void basic_runner_main(pj_test_runner *runner) +{ + pj_test_case *tc; + for (tc = runner->suite->tests.next; + tc != &runner->suite->tests && !runner->stopping; + tc = tc->next) + { + run_test_case(runner, 0, tc); + } +} + +/* Basic runner's callback when a test case completes. */ +static void basic_on_test_complete(pj_test_runner *runner, pj_test_case *tc) +{ + char line[80]; + int len; + + runner->nruns++; + + len = pj_ansi_snprintf( line, sizeof(line), "[%2d/%d] ", + runner->nruns, runner->ntests); + if (len < 1 || len >= sizeof(line)) + len = (int)pj_ansi_strlen(line); + + len += get_completion_line(tc, "", line+len, sizeof(line)-len); + tc->runner->orig_log_writer(3, line, len); +} + +/* Destroy for basic runner */ +static void basic_runner_destroy(pj_test_runner *runner) +{ + /* Nothing to do for basic runner */ + PJ_UNUSED_ARG(runner); +} + +/* Initialize a basic runner. */ +PJ_DEF(void) pj_test_init_basic_runner(pj_test_runner *runner, + const pj_test_runner_param *prm) +{ + pj_bzero(runner, sizeof(*runner)); + if (prm) + pj_memcpy(&runner->prm, prm, sizeof(*prm)); + else + pj_test_runner_param_default(&runner->prm); + runner->main = &basic_runner_main; + runner->destroy = &basic_runner_destroy; + runner->on_test_complete = &basic_on_test_complete; +} + +/******************************* Text Runner *******************************/ + +typedef struct text_runner_t +{ + pj_test_runner base; + pj_mutex_t *mutex; + pj_thread_t **threads; +} text_runner_t; + +/* This is called by thread(s) to get the next test case to run. + * + * Returns: + * - PJ_SUCCESS if we successfully returns next test case (tc) + * - PJ_EPENDING if there is tc left but we must wait for completion of + * exclusive tc + * - PJ_ENOTFOUND if there is no further tests, which in this case the + * thread can exit. + * - other error is not anticipated, and will cause thread to exit. + */ +static pj_status_t text_runner_get_next_test_case(text_runner_t *runner, + pj_test_case **p_test_case) +{ + pj_test_suite *suite; + pj_test_case *cur, *next; + pj_status_t status; + + *p_test_case = NULL; + pj_mutex_lock(runner->mutex); + + suite = runner->base.suite; + + if (runner->base.stopping) { + pj_mutex_unlock(runner->mutex); + return PJ_ENOTFOUND; + } + + cur = get_first_running(&suite->tests); + next = get_next_to_run(&suite->tests); + + if (cur == NULL) { + if (next==NULL) { + status = PJ_ENOTFOUND; + } else { + *p_test_case = next; + /* Mark as running so it won't get picked up by other threads + * when we exit this function + */ + next->runner = &runner->base; + status = PJ_SUCCESS; + } + } else { + /* Test is still running. */ + if ((cur->flags & PJ_TEST_EXCLUSIVE)==0 && next != NULL && + (next->flags & PJ_TEST_EXCLUSIVE)==0) + { + /* Allowed other test to run because test also allows + * parallel test + */ + *p_test_case = next; + /* Mark as running so it won't get picked up by other threads + * when we exit this function + */ + next->runner = &runner->base; + status = PJ_SUCCESS; + } else { + if (next==NULL) { + /* The current case is the last one. The calling thread + * can quit now. + */ + status = PJ_ENOTFOUND; + } else { + /* Current test case or next test do not allow parallel run */ + status = PJ_EPENDING; + } + } + } + + pj_mutex_unlock(runner->mutex); + return status; +} + +typedef struct thread_param_t +{ + text_runner_t *runner; + unsigned tid; +} thread_param_t; + +/* Thread loop */ +static int text_runner_thread_proc(void *arg) +{ + thread_param_t *prm = (thread_param_t*)arg; + text_runner_t *runner = prm->runner; + unsigned tid = prm->tid; + + for (;;) { + pj_test_case *tc; + pj_status_t status; + + status = text_runner_get_next_test_case(runner, &tc); + if (status==PJ_SUCCESS) { + run_test_case(&runner->base, tid, tc); + } else if (status==PJ_EPENDING) { + /* Yeah sleep, but the "correct" solution is probably an order of + * magnitute more complicated, so this is good I think. + */ + pj_thread_sleep(PJ_TEST_THREAD_WAIT_MSEC); + } else { + break; + } + } + + return 0; +} + +/* This is the "main()" function for text runner. */ +static void text_runner_main(pj_test_runner *base) +{ + text_runner_t *runner = (text_runner_t*)base; + thread_param_t tprm ; + unsigned i; + + tprm.runner = runner; + tprm.tid = 0; + + for (i=0; iprm.nthreads; ++i) { + pj_thread_resume(runner->threads[i]); + } + + /* The main thread behaves like another worker thread */ + text_runner_thread_proc(&tprm); + + for (i=0; iprm.nthreads; ++i) { + pj_thread_join(runner->threads[i]); + } +} + +/* text runner's callback when a test case completes. */ +static void text_runner_on_test_complete(pj_test_runner *base, + pj_test_case *tc) +{ + text_runner_t *runner = (text_runner_t*)base; + pj_mutex_lock(runner->mutex); + basic_on_test_complete(base, tc); + pj_mutex_unlock(runner->mutex); +} + +/* text runner destructor */ +static void text_runner_destroy(pj_test_runner *base) +{ + text_runner_t *runner = (text_runner_t*)base; + unsigned i; + + for (i=0; iprm.nthreads; ++i) { + pj_thread_destroy(runner->threads[i]); + } + if (runner->mutex) + pj_mutex_destroy(runner->mutex); + +} + +/* Create text runner */ +PJ_DEF(pj_status_t) pj_test_create_text_runner( + pj_pool_t *pool, + const pj_test_runner_param *prm, + pj_test_runner **p_runner) +{ + text_runner_t *runner; + unsigned i; + pj_status_t status; + + *p_runner = NULL; + + status = unittest_init(); + if (status != PJ_SUCCESS) + return status; + + runner = PJ_POOL_ZALLOC_T(pool, text_runner_t); + runner->base.main = text_runner_main; + runner->base.destroy = text_runner_destroy; + runner->base.on_test_complete = &text_runner_on_test_complete; + + status = pj_mutex_create(pool, "unittest%p", PJ_MUTEX_RECURSE, + &runner->mutex); + if (status != PJ_SUCCESS) + goto on_error; + + if (prm) { + pj_memcpy(&runner->base.prm, prm, sizeof(*prm)); + } else { + pj_test_runner_param_default(&runner->base.prm); + } + runner->base.prm.nthreads = 0; + runner->threads = (pj_thread_t**) pj_pool_calloc(pool, prm->nthreads, + sizeof(pj_thread_t*)); + for (i=0; inthreads; ++i) { + thread_param_t *tprm = PJ_POOL_ZALLOC_T(pool, thread_param_t); + tprm->runner = runner; + tprm->tid = i+1; + status = pj_thread_create(pool, "unittest%p", + text_runner_thread_proc, tprm, + 0, PJ_THREAD_SUSPENDED, + &runner->threads[i]); + if (status != PJ_SUCCESS) + goto on_error; + runner->base.prm.nthreads++; + } + + *p_runner = (pj_test_runner*)runner; + return PJ_SUCCESS; + +on_error: + text_runner_destroy(&runner->base); + return status; +} + diff --git a/pjlib/src/pjlib-test/fifobuf.c b/pjlib/src/pjlib-test/fifobuf.c index fb38460aa4..1d42bf936c 100644 --- a/pjlib/src/pjlib-test/fifobuf.c +++ b/pjlib/src/pjlib-test/fifobuf.c @@ -18,6 +18,7 @@ */ #include "test.h" + /* To prevent warning about "translation unit is empty" * when this test is disabled. */ @@ -27,29 +28,151 @@ int dummy_fifobuf_test; #include -int fifobuf_test() +#define THIS_FILE "fifobuf.c" + +enum { + SZ = sizeof(unsigned), +}; + +static int fifobuf_size_test() +{ + enum { SIZE = 32 }; + char before[8]; + char buffer[SIZE]; + char after[8]; + char zero[8]; + void *p0, *p1; + pj_fifobuf_t fifo; + + pj_bzero(before, sizeof(before)); + pj_bzero(after, sizeof(after)); + pj_bzero(zero, sizeof(zero)); + + pj_fifobuf_init (&fifo, buffer, sizeof(buffer)); + + PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, NULL, return -11); + PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, NULL, return -12); + + p0 = pj_fifobuf_alloc(&fifo, 16); + p1 = pj_fifobuf_alloc(&fifo, 4); + PJ_UNUSED_ARG(p1); + + pj_fifobuf_free(&fifo, p0); + + PJ_TEST_EQ( pj_fifobuf_available_size(&fifo), 16, NULL, return -14); + PJ_TEST_EQ( pj_memcmp(before, zero, sizeof(zero)), 0, NULL, return -18); + PJ_TEST_EQ( pj_memcmp(after, zero, sizeof(zero)), 0, NULL, return -19); + return 0; +} + +static int fifobuf_rolling_test() +{ + enum { + REPEAT=2048, + N=100, + MIN_SIZE = sizeof(pj_list), + MAX_SIZE = 64, + SIZE=(MIN_SIZE+MAX_SIZE)/2*N, + }; + pj_list chunks; + char buffer[SIZE]; + pj_fifobuf_t fifo; + unsigned rep; + + pj_fifobuf_init(&fifo, buffer, sizeof(buffer)); + pj_list_init(&chunks); + + PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, NULL, return -300); + PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, NULL, return -310); + + pj_srand(0); + + /* Repeat the test */ + for (rep=0; rep n) { + chunk = chunks.next; + pj_list_erase(chunk); + pj_fifobuf_free(&fifo, chunk); + } + } + + while (pj_list_size(&chunks)) { + pj_list *chunk = chunks.next; + pj_list_erase(chunk); + pj_fifobuf_free(&fifo, chunk); + } + + PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, NULL, return -350); + PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, NULL, return -360); + + return 0; +} + +static int fifobuf_misc_test() { enum { SIZE = 1024, MAX_ENTRIES = 128, - MIN_SIZE = 4, MAX_SIZE = 64, + MIN_SIZE = 4, MAX_SIZE = 64, LOOP=10000 }; pj_pool_t *pool; pj_fifobuf_t fifo; - unsigned available = SIZE; + pj_size_t available = SIZE; void *entries[MAX_ENTRIES]; void *buffer; int i; pool = pj_pool_create(mem, NULL, SIZE+256, 0, NULL); - if (!pool) - return -10; + PJ_TEST_NOT_NULL(pool, NULL, return -10); buffer = pj_pool_alloc(pool, SIZE); - if (!buffer) - return -20; + PJ_TEST_NOT_NULL(buffer, NULL, return -20); pj_fifobuf_init (&fifo, buffer, SIZE); - // Test 1 + /* Capacity and maximum alloc */ + PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, NULL, return -21); + PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, NULL, return -22); + + entries[0] = pj_fifobuf_alloc(&fifo, SIZE-SZ); + PJ_TEST_NOT_NULL(entries[0], NULL, return -23); + + pj_fifobuf_free(&fifo, entries[0]); + PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, NULL, return -21); + PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, NULL, return -22); + + /* Alignment test */ + for (i=0; i<30; ++i) { + entries[i] = pj_fifobuf_alloc(&fifo, i+1); + PJ_TEST_NOT_NULL(entries[i], NULL, return -50); + //fifobuf is no longer aligned. + //PJ_TEST_EQ(((pj_size_t)entries[i]) % sizeof(unsigned), 0, -60, + // "alignment error"); + } + for (i=0; i<30; ++i) { + PJ_TEST_SUCCESS( pj_fifobuf_free(&fifo, entries[i]), NULL, return -70); + } + + PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, NULL, return -31); + PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, NULL, return -32); + + /* alloc() and free() */ for (i=0; i=MIN_SIZE+4 && count < MAX_ENTRIES;) { - int size = MIN_SIZE+(pj_rand() % MAX_SIZE); - entries[count] = pj_fifobuf_alloc (&fifo, size); - if (entries[count]) { - available -= (size+4); - ++count; - } - } - for (j=0; j + + +#define THIS_FILE "unittest_test.c" + +static char log_buffer[1024], *log_buffer_ptr = log_buffer; +static pj_log_func *old_log_func; + +static void reset_log_buffer() +{ + log_buffer_ptr = log_buffer; + *log_buffer_ptr = '\0'; +} + +#if 0 +static void print_log_buffer(char *title) +{ + printf("------ log buffer %s: ------\n", title); + printf("%s", log_buffer); + printf("%s\n", "------ end buffer: ------"); +} +#else +#define print_log_buffer(title) +#endif + +/* This log callback appends the log to the log_buffer */ +static void log_callback(int level, const char *data, int len) +{ + int max_len = (log_buffer+sizeof(log_buffer))-log_buffer_ptr-1; + + PJ_UNUSED_ARG(level); + + /* make sure len is correct */ + len = strlen(data); + if (len > max_len) + len = max_len; + + pj_ansi_strxcpy(log_buffer_ptr, data, max_len); + log_buffer_ptr += len; +} + +static void start_capture_log() +{ + old_log_func = pj_log_get_log_func(); + pj_log_set_log_func(&log_callback); + reset_log_buffer(); +} + +static void end_capture_log() +{ + pj_log_set_log_func(old_log_func); +} + +static int test_true(int is_true, const char *reason) +{ + PJ_TEST_TRUE(is_true, reason, return -100); + return 0; +} + +static int test_eq(int value0, int value1, const char *reason) +{ + PJ_TEST_EQ(value0, value1, reason, return -200); + return 0; +} + +static int test_success(pj_status_t status, const char *reason) +{ + PJ_TEST_SUCCESS(status, reason, return -300); + return 0; +} + +/* + * Test various assertion tests + */ +static int assertion_tests() +{ + pj_str_t s0, s1; + int ret; + + /* Unary PJ_TEST_TRUE successful */ + ret = test_true(1, NULL); + if (ret != 0) return ret; + + /* PJ_TEST_TRUE successful, reason is specified (but not used) */ + ret = test_true(1, "logic error"); + if (ret != 0) return ret; + + /* PJ_TEST_TRUE fails, without reason */ + reset_log_buffer(); + ret = test_true(0, NULL); + if (ret != -100) return -110; + + /* Check log message, should be something like: + 09:47:09.692 Test "is_true" fails in unittest_test.c:44 + */ + if (pj_ansi_strlen(log_buffer) < 10) return -120; + if (!pj_ansi_strstr(log_buffer, "is_true")) return -121; + if (!pj_ansi_strstr(log_buffer, THIS_FILE)) return -122; + + /* PJ_TEST_TRUE fails, reason is specified */ + reset_log_buffer(); + ret = test_true(0, "logic error"); + if (ret != -100) return -110; + + /* Check log message, should be something like: + 09:47:37.145 Test "is_true" fails in unittest_test.c:44 (logic error) + */ + if (pj_ansi_strlen(log_buffer) < 10) return -130; + if (!pj_ansi_strstr(log_buffer, "is_true")) return -131; + if (!pj_ansi_strstr(log_buffer, THIS_FILE)) return -132; + if (!pj_ansi_strstr(log_buffer, " (logic error)")) return -132; + + /* Binary PJ_TEST_EQ successful */ + ret = test_eq(999, 999, NULL); + if (ret != 0) return ret; + + ret = test_eq(999, 999, "not used"); + if (ret != 0) return ret; + + /* Binary comparison PJ_TEST_EQ fails, reason not given */ + reset_log_buffer(); + ret = test_eq(998, 999, NULL); + if (ret != -200) return -210; + + /* Check log message, should be something like: + 09:47:56.315 Test "value0" (998) == "value1" (999) fails in unittest_test.c:50 + */ + if (pj_ansi_strlen(log_buffer) < 10) return -220; + if (!pj_ansi_strstr(log_buffer, "value0")) return -221; + if (!pj_ansi_strstr(log_buffer, "value1")) return -222; + if (!pj_ansi_strstr(log_buffer, "998")) return -223; + if (!pj_ansi_strstr(log_buffer, "999")) return -224; + if (!pj_ansi_strstr(log_buffer, THIS_FILE)) return -225; + + /* Binary comparison PJ_TEST_EQ fails, reason is given */ + reset_log_buffer(); + ret = test_eq(998, 999, "values are different"); + if (ret != -200) return -250; + + /* Check log message, should be something like: + 09:51:37.866 Test "value0" (998) == "value1" (999) fails in unittest_test.c:50 (values are different) + */ + if (pj_ansi_strlen(log_buffer) < 10) return -260; + if (!pj_ansi_strstr(log_buffer, "value0")) return -261; + if (!pj_ansi_strstr(log_buffer, "value1")) return -262; + if (!pj_ansi_strstr(log_buffer, "998")) return -263; + if (!pj_ansi_strstr(log_buffer, "999")) return -264; + if (!pj_ansi_strstr(log_buffer, THIS_FILE)) return -265; + if (!pj_ansi_strstr(log_buffer, " (values are different)")) return -266; + + /* PJ_TEST_SUCCESS successful scenario */ + ret = test_success(PJ_SUCCESS, NULL); + if (ret != 0) return ret; + + /* PJ_TEST_SUCCESS successful, reason is specified (but not used) */ + ret = test_success(PJ_SUCCESS, "logic error"); + if (ret != 0) return ret; + + /* PJ_TEST_SUCCESS fails, without reason */ + reset_log_buffer(); + ret = test_success(PJ_EPENDING, NULL); + if (ret != -300) return -310; + + /* Check log message, should be something like: + 09:52:22.654 "status" fails in unittest_test.c:56, status=70002 (Pending operation (PJ_EPENDING)) + */ + if (pj_ansi_strlen(log_buffer) < 10) return -320; + if (!pj_ansi_strstr(log_buffer, "Pending operation")) return -321; + if (!pj_ansi_strstr(log_buffer, THIS_FILE)) return -322; + + /* PJ_TEST_SUCCESS fails, reason given */ + reset_log_buffer(); + ret = test_success(PJ_EPENDING, "should be immediate"); + if (ret != -300) return -350; + + /* Check log message, should be something like: + 09:52:49.717 "status" fails in unittest_test.c:56, status=70002 (Pending operation (PJ_EPENDING)) (should be immediate) + */ + if (pj_ansi_strlen(log_buffer) < 10) return -350; + if (!pj_ansi_strstr(log_buffer, "Pending operation")) return -351; + if (!pj_ansi_strstr(log_buffer, THIS_FILE)) return -352; + if (!pj_ansi_strstr(log_buffer, " (should be immediate)")) return -353; + + /* String tests */ + PJ_TEST_STREQ(pj_cstr(&s0, "123456"), pj_cstr(&s1, "123456"), NULL, + return -400); + PJ_TEST_STRICMP(pj_cstr(&s0, "123456"), pj_cstr(&s1, "123456"), ==, 0, + NULL, return -405); + + ret = -1; + PJ_TEST_STREQ(pj_cstr(&s0, "123456"), pj_cstr(&s1, "135"), NULL, + ret=0); + if (ret) + PJ_TEST_EQ(ret, 0, "PJ_TEST_STREQ was expected to fail", return -410); + + PJ_TEST_STRNEQ(pj_cstr(&s0, "123456"), pj_cstr(&s1, "000000"), NULL, + return -420); + + ret = -1; + PJ_TEST_STRNEQ(pj_cstr(&s0, "123456"), pj_cstr(&s1, "123456"), NULL, + ret=0); + if (ret) + PJ_TEST_EQ(ret, 0, "PJ_TEST_STRNEQ was expected to fail", return -410); + + return 0; +} + + +enum test_flags +{ + /* value 0-31 is reserved for test id */ + TEST_LOG_DETAIL = 32, + TEST_LOG_INFO = 64, + TEST_LOG_ALL = (TEST_LOG_DETAIL | TEST_LOG_INFO), + + TEST_RETURN_ERROR = 256, +}; + +/** Dummy test */ +static int func_to_test(void *arg) +{ + unsigned flags = (unsigned)(long)arg; + unsigned test_id = (flags & 31); + + /* Note that for simplicity, make the length of log messages the same + * (otherwise freeing one oldest log may not be enough to fit in the + * later log) + */ + if (flags & TEST_LOG_DETAIL) { + PJ_LOG(4,(THIS_FILE, "Entering func_to_test(%d).....", test_id)); + } + + if (flags & TEST_LOG_INFO) { + PJ_LOG(3,(THIS_FILE, "Performing func_to_test(%d)...", test_id)); + } + + if (flags & TEST_RETURN_ERROR) { + PJ_LOG(1,(THIS_FILE, "Some error in func_to_test(%d)", test_id)); + } + + /* Simulate some work and additional sleep to ensure tests + * completes in correct order + */ + pj_thread_sleep(100+test_id*100); + + return (flags & TEST_RETURN_ERROR) ? -123 : 0; +} + +enum { + /* approx len of each log msg in func_to_test() + * Note that logging adds decor e.g. time, plus overhead in fifobuf. + */ + MSG_LEN = 45 + 4 + sizeof(pj_test_log_item), +}; + +/** + * Simple demonstration on how to use the unittest framework. + * Here we use the unittest framework to test the unittest framework itself. + * We test both the basic and text runner. + */ +static int usage_test(pj_pool_t *pool, pj_bool_t basic, pj_bool_t parallel, + unsigned log_size) +{ + enum { + TEST_CASE_LOG_SIZE = 256, + }; + char test_title[80]; + pj_test_suite suite; + unsigned flags; + char buffer0[TEST_CASE_LOG_SIZE], buffer1[TEST_CASE_LOG_SIZE]; + pj_test_case test_case0, test_case1; + pj_test_runner *runner; + pj_test_runner basic_runner; + pj_test_stat stat; + + /* to differentiate different invocations of this function */ + pj_ansi_snprintf(test_title, sizeof(test_title), + "basic=%d, parallel=%d, log_size=%d", + basic, parallel, log_size); + //PJ_LOG(3,(THIS_FILE, "Unittest usage_test: %s", test_title)); + + PJ_TEST_LTE(log_size, TEST_CASE_LOG_SIZE, test_title, return -1); + + /* Init test suite */ + pj_test_suite_init(&suite); + + if (basic) + flags = 0; + else + flags = parallel? 0 : PJ_TEST_EXCLUSIVE; + + /* Add test case 0. This test case writes some logs and returns + * success. + */ + + pj_test_case_init(&test_case0, "successful test", flags, &func_to_test, + (void*)(long)(0+TEST_LOG_ALL), + buffer0, log_size, NULL); + pj_test_suite_add_case(&suite, &test_case0); + + /* Add test case 1. This test case simulates error. It writes + * error to log and returns non-zero error. + */ + pj_test_case_init(&test_case1, "failure test", flags, &func_to_test, + (void*)(long)(1+TEST_LOG_ALL+TEST_RETURN_ERROR), + buffer1, log_size, NULL); + pj_test_suite_add_case(&suite, &test_case1); + + /* Create runner */ + if (basic) { + runner = &basic_runner; + pj_test_init_basic_runner(runner, NULL); + } else { + pj_test_runner_param prm; + pj_test_runner_param_default(&prm); + prm.nthreads = 4; /* more threads than we need, for testing */ + PJ_TEST_SUCCESS(pj_test_create_text_runner(pool, &prm, &runner), + test_title, return -10); + } + + /* Run runner */ + pj_test_run(runner, &suite); + + /* Runner can be safely destroyed now */ + pj_test_runner_destroy(runner); + + /* test the statistics returned by pj_test_get_stat() */ + pj_bzero(&stat, sizeof(stat)); + pj_test_get_stat(&suite, &stat); + PJ_TEST_EQ(stat.ntests, 2, test_title, return -100); + PJ_TEST_EQ(stat.nruns, 2, test_title, return -110); + PJ_TEST_EQ(stat.nfailed, 1, test_title, return -120); + PJ_TEST_EQ(stat.failed_names[0], test_case1.obj_name, test_title, return -130); + PJ_TEST_EQ(strcmp(stat.failed_names[0], "failure test"), 0, + test_title, return -135); + PJ_TEST_GTE( PJ_TIME_VAL_MSEC(stat.duration), 200, test_title, return -140); + + /* test logging. + * Since gave the test cases buffer to store log messages, we can dump + * the logs now and check the contents. + */ + start_capture_log(); + /* Dumping all test logs. both test 0 and 1 must be present */ + pj_test_display_log_messages(&suite, PJ_TEST_ALL_TESTS | + PJ_TEST_NO_HEADER_FOOTER); + print_log_buffer(test_title); + end_capture_log(); + if (log_size >= MSG_LEN) { + /* We should only have space for the last log */ + PJ_TEST_NOT_NULL(strstr(log_buffer, "Some error in func_to_test(1)"), + test_title, return -201); + PJ_TEST_NOT_NULL(strstr(log_buffer, "Performing func_to_test(0)"), + test_title, return -202); + } else if (log_size < 10) { + /* buffer is too small, writing log will be rejected */ + PJ_TEST_EQ(strstr(log_buffer, "Some error"), NULL, + test_title, return -203); + } else { + /* buffer is small, message will be truncated */ + PJ_TEST_NOT_NULL(strstr(log_buffer, "Some error in"), + test_title, return -204); + } + + if (log_size >= 2*MSG_LEN) { + /* We should have space for two last log messages */ + PJ_TEST_NOT_NULL(strstr(log_buffer, "Performing func_to_test(1)"), + test_title, return -205); + PJ_TEST_NOT_NULL(strstr(log_buffer, "Entering func_to_test(0)"), + test_title, return -206); + } + if (log_size >= 3*MSG_LEN) { + /* We should have space for three last log messages */ + PJ_TEST_NOT_NULL(strstr(log_buffer, "Entering func_to_test(1)"), + test_title, return -207); + } + + /* Dumping only failed test. Only test 1 must be present */ + start_capture_log(); + pj_test_display_log_messages(&suite, PJ_TEST_FAILED_TESTS | + PJ_TEST_NO_HEADER_FOOTER); + print_log_buffer(test_title); + end_capture_log(); + if (log_size >= MSG_LEN) { + PJ_TEST_NOT_NULL(strstr(log_buffer, "Some error in func_to_test(1)"), + test_title, return -211); + } else if (log_size < 10) { + /* buffer is too small, writing log will be rejected */ + PJ_TEST_EQ(strstr(log_buffer, "Some error"), NULL, + test_title, return -212); + } else { + /* buffer is small, message will be truncated */ + PJ_TEST_NOT_NULL(strstr(log_buffer, "Some error in"), + test_title, return -213); + } + if (log_size >= 2*MSG_LEN) { + PJ_TEST_NOT_NULL(strstr(log_buffer, "Performing func_to_test(1)"), + test_title, return -214); + } + if (log_size >= 3*MSG_LEN) { + PJ_TEST_NOT_NULL(strstr(log_buffer, "Entering func_to_test(1)"), + test_title, return -215); + } + PJ_TEST_EQ(strstr(log_buffer, "Entering func_to_test(0)"), NULL, + test_title, return -216); + + /* Dumping only successful test. Only test 0 must be present */ + start_capture_log(); + pj_test_display_log_messages(&suite, PJ_TEST_SUCCESSFUL_TESTS | + PJ_TEST_NO_HEADER_FOOTER); + print_log_buffer(test_title); + end_capture_log(); + if (log_size >= MSG_LEN) { + PJ_TEST_NOT_NULL(strstr(log_buffer, "Performing func_to_test(0)"), + test_title, return -221); + } + if (log_size >= 2*MSG_LEN) { + PJ_TEST_NOT_NULL(strstr(log_buffer, "Entering func_to_test(0)"), + test_title, return -222); + } + PJ_TEST_EQ(strstr(log_buffer, "Entering func_to_test(1)"), NULL, + test_title, return -223); + + return 0; +} + +static int shuffle_test() +{ + enum { N=16, REPEAT=16 }; + pj_test_suite suite; + char test_names[N][16]; + pj_test_case tcs[N], *tc; + unsigned i, repeat, flags[N]; + + for (i=0; iarg, 2, seed_info, return -40); + tc = tc->next; + PJ_TEST_EQ((long)tc->arg, 5, seed_info, return -41); + tc = tc->next; + PJ_TEST_TRUE((long)tc->arg==0 || (long)tc->arg==3, seed_info, + return -42); + tc = tc->next; + PJ_TEST_TRUE((long)tc->arg==0 || (long)tc->arg==3, seed_info, + return -43); + tc = tc->next; + PJ_TEST_EQ((long)tc->arg, 1, seed_info, return -44); + tc = tc->next; + PJ_TEST_EQ((long)tc->arg, 4, seed_info, return -45); + } + + return 0; +} + +static int log_msg_sizes[] = { + 1*MSG_LEN, /* log buffer enough for 1 message */ + 2*MSG_LEN, /* log buffer enough for 2 messages */ + 3*MSG_LEN, /* log buffer enough for 3 message */ + 0, /* no log buffer */ + 64, /* log will be truncated */ +}; + +int unittest_basic_test(void) +{ + int ret, log_level = pj_log_get_level(); + unsigned j; + + if (pj_test_is_under_test()) { + PJ_LOG(1,(THIS_FILE, "Cannot run unittest_test under unit-test!")); + return -1; + } + + /* We wants to get detailed logging */ + pj_log_set_level(4); + + start_capture_log(); + ret = assertion_tests(); + end_capture_log(); + if (ret) goto on_return; + + for (j=0; jsleep); + pj_ansi_strxcat(parallel_msg, prm->id, sizeof(parallel_msg)); + return 0; +} + +/* + * Test that PJ_TEST_EXCLUSIVE flag (or lack of) works. + */ +int unittest_parallel_test() +{ + enum { + MAX_TESTS = 11, + LOG_SIZE = 128, + MS = PJ_TEST_THREAD_WAIT_MSEC, + }; + pj_pool_t *pool; + pj_test_suite suite; + char buffers[MAX_TESTS][LOG_SIZE]; + pj_test_case test_cases[MAX_TESTS]; + pj_test_runner_param prm; + struct parallel_param_t parallel_params[MAX_TESTS] = { + {PJ_TEST_EXCLUSIVE, MS+2*MS, "a"}, + {PJ_TEST_EXCLUSIVE, MS+1*MS, "b"}, /* b have to wait for a */ + {0, MS+7*MS, "c"}, /* c have to wait for b */ + {0, MS+1*MS, "d"}, /* d have to wait for b */ + {0, MS+4*MS, "e"}, /* e have to wait for b */ + {PJ_TEST_EXCLUSIVE, MS+2*MS, "f"}, /* f have to wait for c, d, e */ + {PJ_TEST_EXCLUSIVE, MS+0*MS, "g"}, /* g have to wait for f */ + {PJ_TEST_EXCLUSIVE, MS+5*MS, "h"}, /* h have to wait for g */ + {0, MS+4*MS, "i"}, /* i will finish last */ + {0, MS+2*MS, "j"}, /* i will finish second last */ + {0, MS+0*MS, "k"}, /* i will finish third last */ + }; + const char *correct_msg = "abdecfghkji"; + pj_str_t stmp0, stmp1; + pj_test_runner *runner; + int i; + + pj_test_suite_init(&suite); + PJ_TEST_NOT_NULL((pool=pj_pool_create( mem, NULL, 4000, 4000, NULL)), + NULL, return -1); + + for (i=0; i Date: Wed, 10 Jul 2024 17:27:19 +0700 Subject: [PATCH 040/491] Simple command line argument parsing utility in PJLIB (#4013) --- pjlib/build/pjlib.vcproj | 4 + pjlib/build/pjlib.vcxproj | 1 + pjlib/build/pjlib.vcxproj.filters | 3 + pjlib/include/pj/argparse.h | 205 ++++++++++++++++++++++++++++++ pjlib/include/pjlib.h | 1 + 5 files changed, 214 insertions(+) create mode 100644 pjlib/include/pj/argparse.h diff --git a/pjlib/build/pjlib.vcproj b/pjlib/build/pjlib.vcproj index afc3441a5c..3fb4575bde 100644 --- a/pjlib/build/pjlib.vcproj +++ b/pjlib/build/pjlib.vcproj @@ -12637,6 +12637,10 @@ Name="Header Files" Filter="h;hpp;hxx;hm;inl" > + + diff --git a/pjlib/build/pjlib.vcxproj b/pjlib/build/pjlib.vcxproj index 5a58c29aa5..4467f52e20 100644 --- a/pjlib/build/pjlib.vcxproj +++ b/pjlib/build/pjlib.vcxproj @@ -1036,6 +1036,7 @@ + diff --git a/pjlib/build/pjlib.vcxproj.filters b/pjlib/build/pjlib.vcxproj.filters index 3a823e52ab..2889b4666e 100644 --- a/pjlib/build/pjlib.vcxproj.filters +++ b/pjlib/build/pjlib.vcxproj.filters @@ -442,5 +442,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/pjlib/include/pj/argparse.h b/pjlib/include/pj/argparse.h new file mode 100644 index 0000000000..eeed8571e4 --- /dev/null +++ b/pjlib/include/pj/argparse.h @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2008-2024 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_ARGPARSE_H__ +#define __PJ_ARGPARSE_H__ + +/** + * @file argparse.h + * @brief Command line argument parser + */ +#include +#include +#include + +PJ_BEGIN_DECL + +/** + * Define function to display parsing error. + */ +#ifndef PJ_ARGPARSE_ERROR +# include +# define PJ_ARGPARSE_ERROR(fmt, arg) printf(fmt "\n", arg) +#endif + + +/** + * @defgroup PJ_ARGPARSE Command line argument parser + * @ingroup PJ_MISC + * @{ + * + * This module provides header only utilities to parse command line arguments. + * This is mostly used by PJSIP test and sample apps. Note that there is + * getopt() implementation in PJLIB-UTIL (but it's in PJLIB-UTIL, so it can't + * be used by PJLIB). + * + * Limitations: + * - the utility only supports white space(s) as separator between an option + * and its value. Equal sign is not supported. + * - the utility does not support double dash (--) as separator between options + * and operands. It will keep treating arguments after -- as possible + * options. + */ + +/** + * Peek the next possible option from argv. An argument is considered an + * option if it starts with "-". + * + * @param argv The argv, which must be null terminated. + * + * @return next option or NULL. + */ +PJ_INLINE(char*) pj_argparse_peek_next_option(char *const argv[]) +{ + while (*argv) { + const char *arg = *argv; + if (*arg=='-') { + return *argv; + } + ++argv; + } + return NULL; +} + +/** + * Check that an option exists, without modifying argv. + * + * @param argv The argv, which must be null terminated. + * @param opt The option to find, e.g. "-h", "--help" + * + * @return PJ_TRUE if the option exists, else PJ_FALSE. + */ +PJ_INLINE(pj_bool_t) pj_argparse_exists(char *const argv[], const char *opt) +{ + int i; + for (i=1; argv[i]; ++i) { + if (pj_ansi_strcmp(argv[i], opt)==0) + return PJ_TRUE; + } + return PJ_FALSE; +} + +/** + * Check for an option and if it exists remove that option from argc/argv and + * returns PJ_TRUE. + * + * @param argc Pointer to argc. + * @param argv Null terminated argv. + * @param opt The option to find, e.g. "-h", "--help" + * + * @return PJ_TRUE if the option exists, else PJ_FALSE. + */ +PJ_INLINE(pj_bool_t) pj_argparse_get_bool(int *argc, char *argv[], + const char *opt) +{ + int i; + for (i=1; argv[i]; ++i) { + if (pj_ansi_strcmp(argv[i], opt)==0) { + pj_memmove(&argv[i], &argv[i+1], ((*argc)-i)*sizeof(char*)); + (*argc)--; + return PJ_TRUE; + } + } + return PJ_FALSE; +} + +/** + * Check for an option and if it exists get the value and remove both + * the option and the value from argc/argv. Note that the function only + * supports whitespace(s) as separator between option and value (i.e. equal + * sign is not supported, e.g. "--server=127.0.0.1" will not be parsed + * correctly). + * + * @param argc Pointer to argc. + * @param argv Null terminated argv. + * @param opt The option to find, e.g. "-t", "--type" + * @param ptr_value Pointer to receive the value. + * + * @return - PJ_SUCCESS if the option exists and value is found + * or if the option does not exist + * - PJ_EINVAL if the option exits but value is not found + */ +PJ_INLINE(pj_status_t) pj_argparse_get_str(int *argc, char *argv[], + const char *opt, char **ptr_value) +{ + int i; + for (i=1; argv[i]; ++i) { + if (pj_ansi_strcmp(argv[i], opt)==0) { + pj_memmove(&argv[i], &argv[i+1], ((*argc)-i)*sizeof(char*)); + (*argc)--; + + if (argv[i]) { + char *val = argv[i]; + pj_memmove(&argv[i], &argv[i+1], ((*argc)-i)*sizeof(char*)); + (*argc)--; + *ptr_value = val; + return PJ_SUCCESS; + } else { + PJ_ARGPARSE_ERROR("Error: missing value for %s argument", + opt); + return PJ_EINVAL; + } + } + } + return PJ_SUCCESS; +} + +/** + * Check for an option and if it exists, get the integer value and remove both + * the option and the value from argc/argv. Note that the function only + * supports whitespace(s) as separator between option and value (i.e. equal + * sign is not supported, e.g. "--port=80" will not be parsed correctly). + * + * @param opt The option to find, e.g. "-h", "--help" + * @param argc Pointer to argc. + * @param argv Null terminated argv. + * @param ptr_value Pointer to receive the value. + * + * @return - PJ_SUCCESS if the option exists and value is found + * or if the option does not exist + * - PJ_EINVAL if the option exits but value is not found, + * or if the value is not an integer. + */ +PJ_INLINE(pj_status_t) pj_argparse_get_int(int *argc, char *argv[], + const char *opt, int *ptr_value) +{ + char *endptr, *sval=NULL; + long val; + pj_status_t status = pj_argparse_get_str(argc, argv, opt, &sval); + if (status!=PJ_SUCCESS || !sval) + return status; + + val = strtol(sval, &endptr, 10); + if (*endptr) { + PJ_ARGPARSE_ERROR("Error: invalid value for %s argument", + opt); + return PJ_EINVAL; + } + + *ptr_value = (int)val; + return PJ_SUCCESS; +} + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJ_ARGPARSE_H__ */ + diff --git a/pjlib/include/pjlib.h b/pjlib/include/pjlib.h index 1bc591be51..eb9e757842 100644 --- a/pjlib/include/pjlib.h +++ b/pjlib/include/pjlib.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include From 9b1059922ec52609b55e677e495c49e01c393c5e Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 12 Jul 2024 09:54:36 +0800 Subject: [PATCH 041/491] Fixed Opus decoder PLC setting (#4015) --- pjmedia/src/pjmedia-codec/opus.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pjmedia/src/pjmedia-codec/opus.c b/pjmedia/src/pjmedia-codec/opus.c index 7841792fc4..1264a41ec2 100644 --- a/pjmedia/src/pjmedia-codec/opus.c +++ b/pjmedia/src/pjmedia-codec/opus.c @@ -641,6 +641,7 @@ static pj_status_t codec_open( pjmedia_codec *codec, struct opus_data *opus_data = (struct opus_data *)codec->codec_data; int idx, err; pj_bool_t auto_bit_rate = PJ_TRUE; + pj_bool_t enc_use_plc = PJ_FALSE; PJ_ASSERT_RETURN(codec && attr && opus_data, PJ_EINVAL); @@ -697,7 +698,10 @@ static pj_status_t codec_open( pjmedia_codec *codec, if (idx >= 0) { unsigned plc; plc = (unsigned) pj_strtoul(&attr->setting.enc_fmtp.param[idx].val); - attr->setting.plc = plc > 0? PJ_TRUE: PJ_FALSE; + if (plc > 0) + enc_use_plc = PJ_TRUE; + /* Do not modify local PLC setting as it's used for decoding. */ + // attr->setting.plc = plc > 0? PJ_TRUE: PJ_FALSE } /* Check vad */ @@ -750,7 +754,7 @@ static pj_status_t codec_open( pjmedia_codec *codec, opus_encoder_ctl(opus_data->enc, OPUS_SET_DTX(attr->setting.vad ? 1 : 0)); /* Set PLC */ opus_encoder_ctl(opus_data->enc, - OPUS_SET_INBAND_FEC(attr->setting.plc ? 1 : 0)); + OPUS_SET_INBAND_FEC(enc_use_plc)); /* Set bandwidth */ opus_encoder_ctl(opus_data->enc, OPUS_SET_MAX_BANDWIDTH(get_opus_bw_constant( From f7b30a6c75cb491402b755f45e78ab3900778d56 Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Wed, 17 Jul 2024 04:18:52 -0400 Subject: [PATCH 042/491] Fix implicit fallthrough in ip_helper_generic.c (#4019) The existing code triggers warnings from `-Wimplicit-fallthrough` in LLVM. Adding a break fixes this. --- pjlib/src/pj/ip_helper_generic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pjlib/src/pj/ip_helper_generic.c b/pjlib/src/pj/ip_helper_generic.c index 86d63728af..763304e5f7 100644 --- a/pjlib/src/pj/ip_helper_generic.c +++ b/pjlib/src/pj/ip_helper_generic.c @@ -560,6 +560,7 @@ static pj_status_t get_ipv6_deprecated(unsigned *count, pj_sockaddr addr[]) pj_sockaddr_init(pj_AF_INET6(), &addr[idx], &pj_addr_str, 0); ++idx; + break; default: break; } From c4e6deb6e4aa435969060e83f08f45b281336b89 Mon Sep 17 00:00:00 2001 From: Fil <137893571+filNaj@users.noreply.github.com> Date: Wed, 17 Jul 2024 04:19:34 -0400 Subject: [PATCH 043/491] fix typo (#4017) --- pjsip/include/pjsua-lib/pjsua.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 47ed776df6..a0bdf7d327 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -7013,7 +7013,7 @@ PJ_DECL(pj_status_t) pjsua_im_typing(pjsua_acc_id acc_id, * Application connects one media termination/slot to another by calling * #pjsua_conf_connect() function. This will establish unidirectional * media flow from the source termination to the sink termination. To - * establish bidirectional media flow, application wound need to make another + * establish bidirectional media flow, application would need to make another * call to #pjsua_conf_connect(), this time inverting the source and * destination slots in the parameter. * From 90e848f3c4d7c85a9786dd54f5350c668120532e Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 18 Jul 2024 14:33:27 +0700 Subject: [PATCH 044/491] Add check for media deinitializing in media init completion callback. (#3965) --- pjsip/src/pjsua-lib/pjsua_media.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index d079787a59..d9399a357f 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -1843,6 +1843,12 @@ static pj_status_t call_media_init_cb(pjsua_call_media *call_med, goto on_return; } + /* Check if media is deinitializing */ + if (call_med->call->async_call.med_ch_deinit || !call_med->tp) { + status = PJ_ECANCELLED; + goto on_return; + } + pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_ENCODING, pjsua_var.media_cfg.tx_drop_pct); From 0e0573d0d64aea58d1fb45a19475ae2fb6922f44 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Mon, 29 Jul 2024 13:48:29 +0700 Subject: [PATCH 045/491] Assertion due to deleted account during incoming call in PJSUA2 (#4024) --- pjsip/src/pjsua-lib/pjsua_call.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 4b7e53c4fe..9bd7ca434f 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -2141,7 +2141,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) */ if (dlg->mod_data[pjsua_var.mod.id] == NULL) { /* In PJSUA2, on_incoming_call() may be called from - * on_media_transport_created() hence this might already set + * on_create_media_transport() hence this might already set * to allow notification about fail events via on_call_state() and * on_call_tsx_state(). */ @@ -2167,7 +2167,12 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) * otherwise hangup the call with 480 */ if (pjsua_var.ua_cfg.cb.on_incoming_call) { - pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata); + /* For PJSUA2, avoid invoking this callback again when it has been + * invoked from on_create_media_transport(). + */ + if (call->incoming_data) { + pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata); + } /* Notes: * - the call might be reset when it's rejected or hangup @@ -2178,6 +2183,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) * answer/hangup should have been delayed (see #1923), * so let's process the answer/hangup now. */ + if (call->async_call.call_var.inc_call.hangup) { process_pending_call_hangup(call); } else if (call->med_ch_cb == NULL && call->inv) { From 1caa62506fd74bacf878abd80aeec5ad913a7a2a Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 31 Jul 2024 09:08:00 +0800 Subject: [PATCH 046/491] Adjust disabled media IP version according to offer (#4025) --- pjsip/src/pjsua-lib/pjsua_media.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index d9399a357f..01d4e4ce8f 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -2823,9 +2823,13 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, pj_bool_t use_ipv6; pj_bool_t use_nat64; - use_ipv6 = PJ_HAS_IPV6 && - (pjsua_var.acc[call->acc_id].cfg.ipv6_media_use != - PJSUA_IPV6_DISABLED); + if (rem_sdp) { + use_ipv6 = (get_media_ip_version(call_med, rem_sdp) == 6); + } else { + use_ipv6 = PJ_HAS_IPV6 && + (pjsua_var.acc[call->acc_id].cfg.ipv6_media_use != + PJSUA_IPV6_DISABLED); + } use_nat64 = PJ_HAS_IPV6 && (pjsua_var.acc[call->acc_id].cfg.nat64_opt != PJSUA_NAT64_DISABLED); From 9b79aef4e84a6317740d35f5bc151cf56faf7c33 Mon Sep 17 00:00:00 2001 From: Lanius-collaris <55432068+Lanius-collaris@users.noreply.github.com> Date: Wed, 31 Jul 2024 09:08:27 +0800 Subject: [PATCH 047/491] ip_helper_generic: handle multipart messages containing NLMSG_DONE (#4000) --- pjlib/src/pj/ip_helper_generic.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pjlib/src/pj/ip_helper_generic.c b/pjlib/src/pj/ip_helper_generic.c index 763304e5f7..7a4d854476 100644 --- a/pjlib/src/pj/ip_helper_generic.c +++ b/pjlib/src/pj/ip_helper_generic.c @@ -523,12 +523,12 @@ static pj_status_t get_ipv6_deprecated(unsigned *count, pj_sockaddr addr[]) return PJ_ETOOSMALL; } - if (nlmsg_ptr->nlmsg_type == NLMSG_DONE) - break; - for(; NLMSG_OK(nlmsg_ptr, nlmsg_len); nlmsg_ptr = NLMSG_NEXT(nlmsg_ptr, nlmsg_len)) { + if (nlmsg_ptr->nlmsg_type == NLMSG_DONE) + goto on_return; + struct ifaddrmsg *ifaddrmsg_ptr; struct rtattr *rtattr_ptr; int ifaddrmsg_len; @@ -569,6 +569,7 @@ static pj_status_t get_ipv6_deprecated(unsigned *count, pj_sockaddr addr[]) } } +on_return: close(fd); *count = idx; From 1a82f90f487df70e59560a136196ac3fef27c95c Mon Sep 17 00:00:00 2001 From: LeonidGoltsblat <138720759+LeonidGoltsblat@users.noreply.github.com> Date: Wed, 31 Jul 2024 04:12:04 +0300 Subject: [PATCH 048/491] Get thread locale in pj strerror (#4018) --- pjlib/include/pj/errno.h | 16 +++++++++++++++- pjlib/src/pj/os_error_win32.c | 13 +++++++++++-- pjlib/src/pjlib-test/errno.c | 10 ++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/pjlib/include/pj/errno.h b/pjlib/include/pj/errno.h index c2095482d7..9eb8e97711 100644 --- a/pjlib/include/pj/errno.h +++ b/pjlib/include/pj/errno.h @@ -81,7 +81,9 @@ PJ_BEGIN_DECL /** * Guidelines on error message length. */ -#define PJ_ERR_MSG_SIZE 80 +#ifndef PJ_ERR_MSG_SIZE +# define PJ_ERR_MSG_SIZE 80 +#endif /** * Buffer for title string of #PJ_PERROR(). @@ -90,6 +92,18 @@ PJ_BEGIN_DECL # define PJ_PERROR_TITLE_BUF_SIZE 120 #endif +/** + * On Windows XP and later, force the use of GetThreadLocale() in pj_strerror() + * to obtain the required locale and corresponding language for the platform + * error message string. + * Default value is set to "no" for compatibility reason, which means use + * "User default language". + * + * Default: 0 (no) + */ +#ifndef PJ_STRERROR_USE_WIN_GET_THREAD_LOCALE +# define PJ_STRERROR_USE_WIN_GET_THREAD_LOCALE 0 +#endif /** * Get the last platform error/status, folded into pj_status_t. diff --git a/pjlib/src/pj/os_error_win32.c b/pjlib/src/pj/os_error_win32.c index 25719aad42..a07071d3bb 100644 --- a/pjlib/src/pj/os_error_win32.c +++ b/pjlib/src/pj/os_error_win32.c @@ -173,12 +173,21 @@ int platform_strerror( pj_os_err_type os_errcode, if (!len) { + DWORD dwLanguageId; +#if defined(PJ_STRERROR_USE_WIN_GET_THREAD_LOCALE) && \ + (PJ_STRERROR_USE_WIN_GET_THREAD_LOCALE==1) && (WINVER >= 0x0500) + /* current thread language */ + dwLanguageId = LANGIDFROMLCID(GetThreadLocale()); +#else + /* LANG_USER_DEFAULT - User default language */ + dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); +#endif #if PJ_NATIVE_STRING_IS_UNICODE len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, os_errcode, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + dwLanguageId, wbuf, PJ_ARRAY_SIZE(wbuf), NULL); @@ -190,7 +199,7 @@ int platform_strerror( pj_os_err_type os_errcode, | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, os_errcode, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + dwLanguageId, buf, (int)bufsize, NULL); diff --git a/pjlib/src/pjlib-test/errno.c b/pjlib/src/pjlib-test/errno.c index b5fbc4e532..9231f135ee 100644 --- a/pjlib/src/pjlib-test/errno.c +++ b/pjlib/src/pjlib-test/errno.c @@ -86,7 +86,17 @@ int errno_test(void) pj_set_os_error(rc); /* Whole */ +#if defined(PJ_STRERROR_USE_WIN_GET_THREAD_LOCALE) && (PJ_STRERROR_USE_WIN_GET_THREAD_LOCALE==1) && (WINVER >= 0x0500) + LCID lcid = GetThreadLocale(); + /* set en_US thread locale to obtain "invalid" in errbuff */ + BOOL res = SetThreadLocale(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)); + PJ_UNUSED_ARG(res); +#endif pj_strerror(rc, errbuf, sizeof(errbuf)); +#if defined(PJ_STRERROR_USE_WIN_GET_THREAD_LOCALE) && (PJ_STRERROR_USE_WIN_GET_THREAD_LOCALE==1) && (WINVER >= 0x0500) + /* restore thread locale now */ + SetThreadLocale(lcid); +#endif trim_newlines(errbuf); PJ_LOG(3,(THIS_FILE, "...msg for rc=ERROR_INVALID_DATA: '%s'", errbuf)); if (my_stristr(errbuf, "invalid") == NULL) { From d3d1298802c2e671624f72d07d21ebe609fa164f Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 1 Aug 2024 09:39:17 +0700 Subject: [PATCH 049/491] Miscellaneous fixes (#3853) * Fix typo in SRTP error string * Ignore python __pycache__ dirs in pjsua test * Set PJ_HAS_STDINT_H on VS2010 or later, so logging will not truncate senders (some senders are object names containing 64bit memory address). * Add dialog event files to VS2005 project --------- Co-authored-by: sauwming --- .gitignore | 1 + pjlib/include/pj/compat/os_win32.h | 7 +- pjmedia/src/pjmedia/transport_srtp.c | 2 +- pjsip/build/pjsip_simple.vcproj | 324 ++++++++++++++------------- 4 files changed, 178 insertions(+), 156 deletions(-) diff --git a/.gitignore b/.gitignore index 3a68aeed40..362b9c8902 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,7 @@ pjsip-apps/src/swig/python/pjsua2.py pjsip-apps/src/swig/python/pjsua2_wrap.* # unit tests files +tests/pjsua/**/__pycache__/ tests/pjsua/*.pyc tests/pjsua/scripts-*/*.pyc tests/pjsua/*.log diff --git a/pjlib/include/pj/compat/os_win32.h b/pjlib/include/pj/compat/os_win32.h index d4b410f859..a9b3512c99 100644 --- a/pjlib/include/pj/compat/os_win32.h +++ b/pjlib/include/pj/compat/os_win32.h @@ -45,7 +45,12 @@ #define PJ_HAS_SETJMP_H 1 #define PJ_HAS_STDARG_H 1 #define PJ_HAS_STDDEF_H 1 -#undef PJ_HAS_STDINT_H +/* vs2010/msvc10 or later has stdint.h */ +#if PJ_CC_VER_1 >= 16 +# define PJ_HAS_STDINT_H 1 +#else +# undef PJ_HAS_STDINT_H +#endif #define PJ_HAS_STDIO_H 1 #define PJ_HAS_STDLIB_H 1 #define PJ_HAS_STRING_H 1 diff --git a/pjmedia/src/pjmedia/transport_srtp.c b/pjmedia/src/pjmedia/transport_srtp.c index fc1478a175..fd32a7b63b 100644 --- a/pjmedia/src/pjmedia/transport_srtp.c +++ b/pjmedia/src/pjmedia/transport_srtp.c @@ -472,7 +472,7 @@ const char* get_libsrtp_errstr(int err) "nonce check failed", /* err_status_nonce_bad = 18 */ "couldn't read data", /* err_status_read_fail = 19 */ "couldn't write data", /* err_status_write_fail = 20 */ - "error pasring data", /* err_status_parse_err = 21 */ + "error parsing data", /* err_status_parse_err = 21 */ "error encoding data", /* err_status_encode_err = 22 */ "error while using semaphores", /* err_status_semaphore_err = 23 */ "error while using pfkey" /* err_status_pfkey_err = 24 */ diff --git a/pjsip/build/pjsip_simple.vcproj b/pjsip/build/pjsip_simple.vcproj index a4782149d6..227f0f4ded 100644 --- a/pjsip/build/pjsip_simple.vcproj +++ b/pjsip/build/pjsip_simple.vcproj @@ -11,13 +11,13 @@ Name="Win32" /> - - - - - - - - - - - - + + @@ -882,7 +846,7 @@ /> + + + + @@ -1131,7 +1110,7 @@ /> @@ -1197,7 +1176,7 @@ /> + + + + @@ -1381,7 +1374,7 @@ /> + + + + + + @@ -4145,6 +4153,14 @@ Name="Header Files" Filter="h;hpp;hxx;hm;inl" > + + + + From 26e8d12f865a8d207594e86674fe03a34d9567fd Mon Sep 17 00:00:00 2001 From: sauwming Date: Thu, 1 Aug 2024 10:42:29 +0800 Subject: [PATCH 050/491] Make pjmedia event manager's MAX_EVENTS configurable (#4028) --- pjmedia/include/pjmedia/config.h | 11 ++++++++++- pjmedia/src/pjmedia/event.c | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h index 4325858b6a..8408f0ae86 100644 --- a/pjmedia/include/pjmedia/config.h +++ b/pjmedia/include/pjmedia/config.h @@ -65,12 +65,21 @@ #endif /** - * Memory increment for evnt manager. + * Memory increment for event manager. */ #ifndef PJMEDIA_POOL_INC_EVTMGR # define PJMEDIA_POOL_INC_EVTMGR 500 #endif +/** + * Maximum number of events that can be handled by event manager. + * + * Default: 16 + */ +#ifndef PJMEDIA_EVENT_MAX_EVENTS +# define PJMEDIA_EVENT_MAX_EVENTS 16 +#endif + /** * Specify whether we prefer to use audio switch board rather than * conference bridge. diff --git a/pjmedia/src/pjmedia/event.c b/pjmedia/src/pjmedia/event.c index 30ae9ce311..30500848cb 100644 --- a/pjmedia/src/pjmedia/event.c +++ b/pjmedia/src/pjmedia/event.c @@ -15,6 +15,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include #include #include @@ -26,7 +27,7 @@ #define THIS_FILE "event.c" -#define MAX_EVENTS 16 +#define MAX_EVENTS PJMEDIA_EVENT_MAX_EVENTS /* Enable some tracing */ // #define EVENT_TRACE From f5d890aa3463a096d7110ae935c67d6249d2f662 Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 2 Aug 2024 12:09:27 +0800 Subject: [PATCH 051/491] SIP transport: call callback when data is dropped due to invalid headers (#4026) --- pjsip/src/pjsip/sip_transport.c | 48 +++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index 27c8176093..b56fb3e295 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -2157,20 +2157,46 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, msg_status = pjsip_find_msg(current_pkt, remaining_len, PJ_FALSE, &msg_fragment_size); if (msg_status != PJ_SUCCESS) { - if (remaining_len == PJSIP_MAX_PKT_LEN) { - mgr->on_rx_msg(mgr->endpt, PJSIP_ERXOVERFLOW, rdata); - - /* Notify application about the message overflow */ + pj_status_t dd_status = msg_status; + + if (msg_status == PJSIP_EPARTIALMSG) { + if (remaining_len != PJSIP_MAX_PKT_LEN) { + /* We only get partial message: Not enough data in + * packet. + */ + return total_processed; + } + /* Overflow, buffer too small. */ + dd_status = PJSIP_ERXOVERFLOW; + } else { + /* msg_status == PJSIP_EMISSINGHDR || + msg_status == PJSIP_EINVALIDHDR + */ + char errbuf[128]; + pj_str_t errstr; + + errstr = pjsip_strerror(msg_status, errbuf, sizeof(errbuf) - 1); + errstr.ptr[errstr.slen] = '\0'; + PJ_LOG(4, (THIS_FILE, "%s Unable to match whole message: %s", + rdata->tp_info.transport->obj_name, errstr.ptr)); + } + + if (1) { + mgr->on_rx_msg(mgr->endpt, dd_status, rdata); + + /* Notify application */ if (mgr->tp_drop_data_cb) { pjsip_tp_dropped_data dd; pj_bzero(&dd, sizeof(dd)); dd.tp = tr; dd.data = current_pkt; dd.len = msg_fragment_size; - dd.status = PJSIP_ERXOVERFLOW; + dd.status = dd_status; (*mgr->tp_drop_data_cb)(&dd); } + } + if (msg_status == PJSIP_EPARTIALMSG) { if (rdata->tp_info.transport->idle_timer.id == INITIAL_IDLE_TIMER_ID) { @@ -2183,14 +2209,14 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, rdata->tp_info.transport->obj_name)); pjsip_transport_shutdown(rdata->tp_info.transport); + } else { + PJ_LOG(4, (THIS_FILE, "Insufficient buffer to receive " + "incoming packet.")); } - - /* Exhaust all data. */ - return rdata->pkt_info.len; - } else { - /* Not enough data in packet. */ - return total_processed; } + + /* Exhaust all data. */ + return rdata->pkt_info.len; } } From f4f3f43acb18e98123fd846bba9ecf7eadcc42ba Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 13 Aug 2024 11:33:47 +0700 Subject: [PATCH 052/491] Fix assertion in quitting pjsua app when AVI feature is disabled (in compile-time) but configured in pjsua app param. (#4036) --- pjsip-apps/src/pjsua/pjsua_app.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c index 37260251e7..83c41cfe94 100644 --- a/pjsip-apps/src/pjsua/pjsua_app.c +++ b/pjsip-apps/src/pjsua/pjsua_app.c @@ -1718,6 +1718,11 @@ static pj_status_t app_init(void) } } #else + for (i=0; i Date: Tue, 13 Aug 2024 11:45:28 +0700 Subject: [PATCH 053/491] Assertion when sending SUBSCRIBE failed synchronously (#3985) * Handle possibility of reset buddy states after SUBSCRIBE send failure --- pjsip/src/pjsua-lib/pjsua_pres.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c index f273c1bc5d..083782e062 100644 --- a/pjsip/src/pjsua-lib/pjsua_pres.c +++ b/pjsip/src/pjsua-lib/pjsua_pres.c @@ -1995,6 +1995,7 @@ static void subscribe_buddy(pjsua_buddy_id buddy_id, pjsip_tpselector tp_sel; pj_status_t status; const char *sub_str = presence? "presence": "dialog event"; + pjsip_dialog *dlg; /* Event subscription callback. */ pj_bzero(&pres_callback, sizeof(pres_callback)); @@ -2139,14 +2140,22 @@ static void subscribe_buddy(pjsua_buddy_id buddy_id, pjsua_process_msg_data(tdata, NULL); + /* Send request. Note that if the send operation fails sync-ly, e.g: + * gethostbyname() error, tsx callback may have been invoked which may + * get the subscription terminated and the buddy states reset, we need + * to be prepared for such scenario here. + */ + dlg = buddy->dlg; + if (presence) { status = pjsip_pres_send_request(buddy->sub, tdata); } else { status = pjsip_dlg_event_send_request(buddy->sub, tdata); } if (status != PJ_SUCCESS) { - pjsip_dlg_dec_lock(buddy->dlg); - pjsip_pres_terminate(buddy->sub, PJ_FALSE); + pjsip_dlg_dec_lock(dlg); + if (buddy->sub) + pjsip_pres_terminate(buddy->sub, PJ_FALSE); buddy->sub = NULL; pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE", status); From de5b5c8494b50fad8eeceead58346f2d5cbead8d Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 15 Aug 2024 08:57:42 +0700 Subject: [PATCH 054/491] Total invalidate deleted PJSUA account. (#4035) --- pjsip/src/pjsua-lib/pjsua_acc.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index 0326f5480c..7c2d6c8b7e 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -709,18 +709,14 @@ PJ_DEF(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_id) /* Delete server presence subscription */ pjsua_pres_delete_acc(acc_id, 0); - /* Release account pool */ + /* Release & wipe account pool */ if (acc->pool) { - pj_pool_release(acc->pool); - acc->pool = NULL; + pj_pool_secure_release(&acc->pool); } /* Invalidate */ - acc->valid = PJ_FALSE; - pj_bzero(&acc->via_addr, sizeof(acc->via_addr)); - acc->via_tp = NULL; - acc->next_rtp_port = 0; - acc->ip_change_op = PJSUA_IP_CHANGE_OP_NULL; + pj_bzero(acc, sizeof(*acc)); + acc->index = acc_id; /* Remove from array */ for (i=0; i Date: Thu, 15 Aug 2024 14:35:30 +0700 Subject: [PATCH 055/491] Prevent recreating TLS transport when answering a call after network change (#4033) * Prevent recreating transport when sending Response after network change * Add check if initial_dest is already set --- pjsip/src/pjsip/sip_dialog.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c index 2c40106a66..a485a0dffe 100644 --- a/pjsip/src/pjsip/sip_dialog.c +++ b/pjsip/src/pjsip/sip_dialog.c @@ -1630,6 +1630,15 @@ PJ_DEF(pj_status_t) pjsip_dlg_send_response( pjsip_dialog *dlg, pj_assert(status == PJ_SUCCESS); } + /* Copy the initial destination host to tdata. This information can be + * used later by transport for transport selection. + */ + if (!tdata->dest_info.name.slen && dlg->initial_dest.slen) { + pj_strdup(tdata->pool, &tdata->dest_info.name, &dlg->initial_dest); + PJ_LOG(5, (THIS_FILE, "Setting initial dest %.*s", + (int)dlg->initial_dest.slen, dlg->initial_dest.ptr)); + } + /* Ask transaction to send the response */ status = pjsip_tsx_send_msg(tsx, tdata); From 5d7e2748ce6ca3f58e5310f8d7d8f78e94e4db90 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Fri, 16 Aug 2024 10:31:16 +0700 Subject: [PATCH 056/491] Don't restart retransmit timer when transport is pending (IP change scenario) (#4037) --- pjsip/src/pjsip/sip_transaction.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c index a669f8f7a8..5d6031221d 100644 --- a/pjsip/src/pjsip/sip_transaction.c +++ b/pjsip/src/pjsip/sip_transaction.c @@ -2369,6 +2369,8 @@ static void tsx_tp_state_callback( pjsip_transport *tp, tsx = (pjsip_transaction*)info->user_data; + tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT); + /* Post the event for later processing, to avoid deadlock. * See https://github.com/pjsip/pjproject/issues/1646 */ @@ -2692,6 +2694,13 @@ static pj_status_t tsx_retransmit( pjsip_transaction *tsx, int resched) return status; } + /* Cancel retransmission timer if transport is pending. */ + if (resched && (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT)) + { + tsx_cancel_timer( tsx, &tsx->retransmit_timer ); + tsx->transport_flag |= TSX_HAS_PENDING_RESCHED; + } + return PJ_SUCCESS; } From 0c6b9fb18e2992991059f8f93dd3d89d20954fcd Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Mon, 19 Aug 2024 16:24:29 +0300 Subject: [PATCH 057/491] pcaputil: Quit gracefully on end-of-file (#4044) --- pjsip-apps/src/samples/pcaputil.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/pjsip-apps/src/samples/pcaputil.c b/pjsip-apps/src/samples/pcaputil.c index d1621fabbe..0a2acbf2d6 100644 --- a/pjsip-apps/src/samples/pcaputil.c +++ b/pjsip-apps/src/samples/pcaputil.c @@ -132,11 +132,11 @@ static void err_exit(const char *title, pj_status_t status) } while (0) -static void read_rtp(pj_uint8_t *buf, pj_size_t bufsize, - pjmedia_rtp_hdr **rtp, - pj_uint8_t **payload, - unsigned *payload_size, - pj_bool_t check_pt) +static int read_rtp(pj_uint8_t *buf, pj_size_t bufsize, + pjmedia_rtp_hdr **rtp, + pj_uint8_t **payload, + unsigned *payload_size, + pj_bool_t check_pt) { pj_status_t status; @@ -154,8 +154,11 @@ static void read_rtp(pj_uint8_t *buf, pj_size_t bufsize, pjmedia_rtp_status seq_st; status = pj_pcap_read_udp(app.pcap, NULL, buf, &sz); - if (status != PJ_SUCCESS) + if (status != PJ_SUCCESS) { + if (status == PJ_EEOF) + return PJ_FALSE; err_exit("Error reading PCAP file", status); + } /* Decode RTP packet to make sure that this is an RTP packet. * We will decode it again to get the payload after we do @@ -225,6 +228,7 @@ static void read_rtp(pj_uint8_t *buf, pj_size_t bufsize, /* We have good packet */ break; } + return PJ_TRUE; } pjmedia_frame play_frm; @@ -387,8 +391,10 @@ static void pcap2wav(const pj_str_t *codec, } /* Read next packet */ - read_rtp(pkt1.buffer, sizeof(pkt1.buffer), &pkt1.rtp, - &pkt1.payload, &pkt1.payload_len, PJ_TRUE); + if (!read_rtp(pkt1.buffer, sizeof(pkt1.buffer), &pkt1.rtp, + &pkt1.payload, &pkt1.payload_len, PJ_TRUE)) { + break; + } /* Fill in the gap (if any) between pkt0 and pkt1 */ ts_gap = pj_ntohl(pkt1.rtp->ts) - pj_ntohl(pkt0.rtp->ts) - From ae608ccc3e3d42bfe06e72ba801a107f21779339 Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 21 Aug 2024 08:12:59 +0800 Subject: [PATCH 058/491] Save the original user and password part when parsing URI (#4043) --- pjsip/include/pjsip/sip_uri.h | 1 + pjsip/src/pjsip/sip_parser.c | 7 +++++++ pjsip/src/pjsip/sip_uri.c | 20 +++++++++++++------- pjsip/src/test/uri_test.c | 1 + 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/pjsip/include/pjsip/sip_uri.h b/pjsip/include/pjsip/sip_uri.h index 2459bf5fd0..945ef97325 100644 --- a/pjsip/include/pjsip/sip_uri.h +++ b/pjsip/include/pjsip/sip_uri.h @@ -339,6 +339,7 @@ typedef struct pjsip_sip_uri pjsip_uri_vptr *vptr; /**< Pointer to virtual function table.*/ pj_str_t user; /**< Optional user part. */ pj_str_t passwd; /**< Optional password part. */ + pj_str_t orig_userpass; /**< Optional original user&pass. */ pj_str_t host; /**< Host part, always exists. */ int port; /**< Optional port number, or zero. */ pj_str_t user_param; /**< Optional user parameter */ diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c index 54ba6dbba1..3da90dc85a 100644 --- a/pjsip/src/pjsip/sip_parser.c +++ b/pjsip/src/pjsip/sip_parser.c @@ -1531,7 +1531,14 @@ static void* int_parse_sip_url( pj_scanner *scanner, } if (int_is_next_user(scanner)) { + char *start = scanner->curptr; + pj_str_t orig; + + start = scanner->curptr; int_parse_user_pass(scanner, pool, &url->user, &url->passwd); + + pj_strset3(&orig, start, scanner->curptr - 1); + pj_strdup(pool, &url->orig_userpass, &orig); } /* Get host:port */ diff --git a/pjsip/src/pjsip/sip_uri.c b/pjsip/src/pjsip/sip_uri.c index f823d12bd4..ed28a004cd 100644 --- a/pjsip/src/pjsip/sip_uri.c +++ b/pjsip/src/pjsip/sip_uri.c @@ -269,13 +269,18 @@ static pj_ssize_t pjsip_url_print( pjsip_uri_context_e context, /* Print "user:password@", if any. */ if (url->user.slen) { - const pj_cis_t *spec = pjsip_cfg()->endpt.allow_tx_hash_in_uri ? - &pc->pjsip_USER_SPEC_LENIENT : - &pc->pjsip_USER_SPEC; - copy_advance_escape(buf, url->user, *spec); - if (url->passwd.slen) { - copy_advance_char_check(buf, ':'); - copy_advance_escape(buf, url->passwd, pc->pjsip_PASSWD_SPEC); + /* Use the URI's original username and password, if any. */ + if (url->orig_userpass.slen) { + copy_advance_check(buf, url->orig_userpass); + } else { + const pj_cis_t *spec = pjsip_cfg()->endpt.allow_tx_hash_in_uri ? + &pc->pjsip_USER_SPEC_LENIENT : + &pc->pjsip_USER_SPEC; + copy_advance_escape(buf, url->user, *spec); + if (url->passwd.slen) { + copy_advance_char_check(buf, ':'); + copy_advance_escape(buf, url->passwd, pc->pjsip_PASSWD_SPEC); + } } copy_advance_char_check(buf, '@'); @@ -508,6 +513,7 @@ PJ_DEF(void) pjsip_sip_uri_assign(pj_pool_t *pool, pjsip_sip_uri *url, { pj_strdup( pool, &url->user, &rhs->user); pj_strdup( pool, &url->passwd, &rhs->passwd); + pj_strdup( pool, &url->orig_userpass, &rhs->orig_userpass); pj_strdup( pool, &url->host, &rhs->host); url->port = rhs->port; pj_strdup( pool, &url->user_param, &rhs->user_param); diff --git a/pjsip/src/test/uri_test.c b/pjsip/src/test/uri_test.c index 41fc977267..e4098dc603 100644 --- a/pjsip/src/test/uri_test.c +++ b/pjsip/src/test/uri_test.c @@ -571,6 +571,7 @@ static pjsip_uri *create_uri14(pj_pool_t *pool) pj_strdup2(pool, &name_addr->display, "This is -. !% *_+`'~ me"); pj_strdup2(pool, &url->user, "a19A&=+$,;?/,"); pj_strdup2(pool, &url->passwd, "@a&Zz=+$,"); + pj_strdup2(pool, &url->orig_userpass, "a19A&=+$,;?/%2c:%40a&Zz=+$,"); pj_strdup2(pool, &url->host, "my_proxy09.MY-domain.com"); url->port = 9801; return (pjsip_uri*)name_addr; From d595d6a572724c2f8a129f02587c7dae2c6ed69b Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Wed, 21 Aug 2024 06:00:51 +0300 Subject: [PATCH 059/491] pcaputil: Add arguments for opus channel count and clock rate (#4045) --- pjsip-apps/src/samples/pcaputil.c | 155 +++++++++++++++++++----------- 1 file changed, 100 insertions(+), 55 deletions(-) diff --git a/pjsip-apps/src/samples/pcaputil.c b/pjsip-apps/src/samples/pcaputil.c index 0a2acbf2d6..cf8e425bce 100644 --- a/pjsip-apps/src/samples/pcaputil.c +++ b/pjsip-apps/src/samples/pcaputil.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include @@ -51,6 +51,10 @@ static const char *USAGE = " AES_CM_128_HMAC_SHA1_80 \n" " AES_CM_128_HMAC_SHA1_32\n" " --srtp-key=KEY, -k Set the base64 key to decrypt SRTP packets.\n" +#if PJMEDIA_HAS_OPUS_CODEC +" --opus-ch=CH Opus channel count \n" +" --opus-clock-rate=CR Opus clock rate \n" +#endif "\n" "Options for playing to audio device:\n" "" @@ -79,6 +83,19 @@ static struct app pj_bool_t rtp_sess_init; } app; +struct args +{ + pj_str_t codec; + pj_str_t wav_filename; + pjmedia_aud_dev_index dev_id; + pj_str_t srtp_crypto; + pj_str_t srtp_key; +#if PJMEDIA_HAS_OPUS_CODEC + int opus_clock_rate; + int opus_ch; +#endif +}; + static void cleanup() { @@ -164,7 +181,7 @@ static int read_rtp(pj_uint8_t *buf, pj_size_t bufsize, * We will decode it again to get the payload after we do * SRTP decoding */ - status = pjmedia_rtp_decode_rtp(&app.rtp_sess, buf, (int)sz, &r, + status = pjmedia_rtp_decode_rtp(&app.rtp_sess, buf, (int)sz, &r, &p, payload_size); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; @@ -177,12 +194,12 @@ static int read_rtp(pj_uint8_t *buf, pj_size_t bufsize, #if PJMEDIA_HAS_SRTP if (app.srtp) { int len = (int)sz; - status = pjmedia_transport_srtp_decrypt_pkt(app.srtp, PJ_TRUE, + status = pjmedia_transport_srtp_decrypt_pkt(app.srtp, PJ_TRUE, buf, &len); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); - printf("SRTP packet decryption failed, skipping packet: %s\n", + printf("SRTP packet decryption failed, skipping packet: %s\n", errmsg); continue; } @@ -252,7 +269,7 @@ static pj_status_t play_cb(void *user_data, pjmedia_frame *f) PJ_UNUSED_ARG(user_data); if (!play_frm_ready) { - PJ_LOG(3, ("play_cb()", "Warning! Play frame not ready")); + PJ_LOG(3, ("play_cb()", "Warning! Play frame not ready")); return PJ_SUCCESS; } @@ -263,11 +280,7 @@ static pj_status_t play_cb(void *user_data, pjmedia_frame *f) return PJ_SUCCESS; } -static void pcap2wav(const pj_str_t *codec, - const pj_str_t *wav_filename, - pjmedia_aud_dev_index dev_id, - const pj_str_t *srtp_crypto, - const pj_str_t *srtp_key) +static void pcap2wav(const struct args *args) { const pj_str_t WAV = {".wav", 4}; struct pkt @@ -288,24 +301,21 @@ static void pcap2wav(const pj_str_t *codec, /* Create SRTP transport is needed */ #if PJMEDIA_HAS_SRTP - if (srtp_crypto->slen) { + if (args->srtp_crypto.slen) { pjmedia_srtp_crypto crypto; pjmedia_transport *tp; pj_bzero(&crypto, sizeof(crypto)); - crypto.key = *srtp_key; - crypto.name = *srtp_crypto; + crypto.key = args->srtp_key; + crypto.name = args->srtp_crypto; T( pjmedia_transport_loop_create(app.mept, &tp) ); T( pjmedia_transport_srtp_create(app.mept, tp, NULL, &app.srtp) ); T( pjmedia_transport_srtp_start(app.srtp, &crypto, &crypto) ); } -#else - PJ_UNUSED_ARG(srtp_crypto); - PJ_UNUSED_ARG(srtp_key); #endif /* Read first packet */ - read_rtp(pkt0.buffer, sizeof(pkt0.buffer), &pkt0.rtp, + read_rtp(pkt0.buffer, sizeof(pkt0.buffer), &pkt0.rtp, &pkt0.payload, &pkt0.payload_len, PJ_FALSE); cmgr = pjmedia_endpt_get_codec_mgr(app.mept); @@ -317,7 +327,7 @@ static void pcap2wav(const pj_str_t *codec, } else { unsigned cnt = 2; const pjmedia_codec_info *info[2]; - T( pjmedia_codec_mgr_find_codecs_by_id(cmgr, codec, &cnt, + T( pjmedia_codec_mgr_find_codecs_by_id(cmgr, &args->codec, &cnt, info, NULL) ); if (cnt != 1) err_exit("Codec ID must be specified and unique!", 0); @@ -332,23 +342,31 @@ static void pcap2wav(const pj_str_t *codec, T( pjmedia_codec_open(app.codec, ¶m) ); /* Init audio device or WAV file */ - samples_per_frame = ci->clock_rate * param.info.frm_ptime / 1000; - if (pj_strcmp2(wav_filename, "-") == 0) { + samples_per_frame = param.info.clock_rate * param.info.frm_ptime / 1000; + if (pj_strcmp2(&args->wav_filename, "-") == 0) { pjmedia_aud_param aud_param; /* Open audio device */ - T( pjmedia_aud_dev_default_param(dev_id, &aud_param) ); + T( pjmedia_aud_dev_default_param(args->dev_id, &aud_param) ); aud_param.dir = PJMEDIA_DIR_PLAYBACK; - aud_param.channel_count = ci->channel_cnt; - aud_param.clock_rate = ci->clock_rate; + aud_param.channel_count = param.info.channel_cnt; + aud_param.clock_rate = param.info.clock_rate; aud_param.samples_per_frame = samples_per_frame; - T( pjmedia_aud_stream_create(&aud_param, NULL, &play_cb, +#if PJMEDIA_HAS_OPUS_CODEC + if (!pj_stricmp2(&args->codec, "opus")) { + if (args->opus_clock_rate > 0) + aud_param.clock_rate = args->opus_clock_rate; + if (args->opus_ch > 0) + aud_param.channel_count = args->opus_ch; + } +#endif + T( pjmedia_aud_stream_create(&aud_param, NULL, &play_cb, NULL, &app.aud_strm) ); T( pjmedia_aud_stream_start(app.aud_strm) ); - } else if (pj_stristr(wav_filename, &WAV)) { + } else if (pj_stristr(&args->wav_filename, &WAV)) { /* Open WAV file */ - T( pjmedia_wav_writer_port_create(app.pool, wav_filename->ptr, - ci->clock_rate, ci->channel_cnt, + T( pjmedia_wav_writer_port_create(app.pool, args->wav_filename.ptr, + param.info.clock_rate, param.info.channel_cnt, samples_per_frame, param.info.pcm_bits_per_sample, 0, 0, &app.wav) ); @@ -370,7 +388,7 @@ static void pcap2wav(const pj_str_t *codec, /* Parse first packet */ ts.u64 = 0; frame_cnt = PJ_ARRAY_SIZE(frames); - T( pjmedia_codec_parse(app.codec, pkt0.payload, pkt0.payload_len, + T( pjmedia_codec_parse(app.codec, pkt0.payload, pkt0.payload_len, &ts, &frame_cnt, frames) ); /* Decode and write to WAV file */ @@ -379,7 +397,7 @@ static void pcap2wav(const pj_str_t *codec, pcm_frame.buf = pcm; pcm_frame.size = samples_per_frame * 2; - T( pjmedia_codec_decode(app.codec, &frames[i], + T( pjmedia_codec_decode(app.codec, &frames[i], (unsigned)pcm_frame.size, &pcm_frame) ); if (app.wav) { T( pjmedia_port_put_frame(app.wav, &pcm_frame) ); @@ -405,7 +423,7 @@ static void pcap2wav(const pj_str_t *codec, pcm_frame.size = samples_per_frame * 2; if (app.codec->op->recover) { - T( pjmedia_codec_recover(app.codec, (unsigned)pcm_frame.size, + T( pjmedia_codec_recover(app.codec, (unsigned)pcm_frame.size, &pcm_frame) ); } else { pj_bzero(pcm_frame.buf, pcm_frame.size); @@ -419,7 +437,7 @@ static void pcap2wav(const pj_str_t *codec, } ts_gap -= samples_per_frame; } - + /* Next */ pkt0 = pkt1; pkt0.rtp = (pjmedia_rtp_hdr*)pkt0.buffer; @@ -430,32 +448,51 @@ static void pcap2wav(const pj_str_t *codec, int main(int argc, char *argv[]) { - pj_str_t input, output, srtp_crypto, srtp_key, codec; - pjmedia_aud_dev_index dev_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV; + pj_str_t input; pj_pcap_filter filter; pj_status_t status; - - enum { - OPT_SRC_IP = 1, OPT_DST_IP, OPT_SRC_PORT, OPT_DST_PORT, - OPT_CODEC, OPT_PLAY_DEV_ID + struct args args; + + enum { + OPT_SRC_IP = 1, + OPT_DST_IP, + OPT_SRC_PORT, + OPT_DST_PORT, + OPT_CODEC, + OPT_PLAY_DEV_ID, +#if PJMEDIA_HAS_OPUS_CODEC + OPT_OPUS_CH = 'C', + OPT_OPUS_CLOCK_RATE = 'K', +#endif + OPT_SRTP_CRYPTO = 'c', + OPT_SRTP_KEY = 'k' }; struct pj_getopt_option long_options[] = { - { "srtp-crypto", 1, 0, 'c' }, - { "srtp-key", 1, 0, 'k' }, + { "srtp-crypto", 1, 0, OPT_SRTP_CRYPTO }, + { "srtp-key", 1, 0, OPT_SRTP_KEY }, { "src-ip", 1, 0, OPT_SRC_IP }, { "dst-ip", 1, 0, OPT_DST_IP }, { "src-port", 1, 0, OPT_SRC_PORT }, { "dst-port", 1, 0, OPT_DST_PORT }, { "codec", 1, 0, OPT_CODEC }, { "play-dev-id", 1, 0, OPT_PLAY_DEV_ID }, +#if PJMEDIA_HAS_OPUS_CODEC + { "opus-ch", 1, 0, OPT_OPUS_CH }, + { "opus-clock-rate", 1, 0, OPT_OPUS_CLOCK_RATE }, +#endif { NULL, 0, 0, 0} }; int c; int option_index; char key_bin[32]; - srtp_crypto.slen = srtp_key.slen = 0; - codec.slen = 0; + args.srtp_crypto.slen = args.srtp_key.slen = 0; + args.codec.slen = 0; + args.dev_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV; +#if PJMEDIA_HAS_OPUS_CODEC + args.opus_clock_rate = -1; + args.opus_ch = -1; +#endif pj_pcap_filter_default(&filter); filter.link = PJ_PCAP_LINK_TYPE_ETH; @@ -465,19 +502,19 @@ int main(int argc, char *argv[]) pj_optind = 0; while((c=pj_getopt_long(argc,argv, "c:k:", long_options, &option_index))!=-1) { switch (c) { - case 'c': - srtp_crypto = pj_str(pj_optarg); + case OPT_SRTP_CRYPTO: + args.srtp_crypto = pj_str(pj_optarg); break; - case 'k': + case OPT_SRTP_KEY: { int key_len = sizeof(key_bin); - srtp_key = pj_str(pj_optarg); - if (pj_base64_decode(&srtp_key, (pj_uint8_t*)key_bin, &key_len)) { + args.srtp_key = pj_str(pj_optarg); + if (pj_base64_decode(&args.srtp_key, (pj_uint8_t*)key_bin, &key_len)) { puts("Error: invalid key"); return 1; } - srtp_key.ptr = key_bin; - srtp_key.slen = key_len; + args.srtp_key.ptr = key_bin; + args.srtp_key.slen = key_len; } break; case OPT_SRC_IP: @@ -501,11 +538,19 @@ int main(int argc, char *argv[]) filter.dst_port = pj_htons((pj_uint16_t)atoi(pj_optarg)); break; case OPT_CODEC: - codec = pj_str(pj_optarg); + args.codec = pj_str(pj_optarg); break; case OPT_PLAY_DEV_ID: - dev_id = atoi(pj_optarg); + args.dev_id = atoi(pj_optarg); + break; +#if PJMEDIA_HAS_OPUS_CODEC + case OPT_OPUS_CLOCK_RATE: + args.opus_clock_rate = atoi(pj_optarg); break; + case OPT_OPUS_CH: + args.opus_ch = atoi(pj_optarg); + break; +#endif default: puts("Error: invalid option"); return 1; @@ -517,15 +562,15 @@ int main(int argc, char *argv[]) return 1; } - if (!(srtp_crypto.slen) != !(srtp_key.slen)) { + if (!(args.srtp_crypto.slen) != !(args.srtp_key.slen)) { puts("Error: both SRTP crypto and key must be specified"); puts(USAGE); return 1; } input = pj_str(argv[pj_optind]); - output = pj_str(argv[pj_optind+1]); - + args.wav_filename = pj_str(argv[pj_optind+1]); + T( pj_init() ); pj_caching_pool_init(&app.cp, NULL, 0); @@ -537,7 +582,7 @@ int main(int argc, char *argv[]) T( pj_pcap_open(app.pool, input.ptr, &app.pcap) ); T( pj_pcap_set_filter(app.pcap, &filter) ); - pcap2wav(&codec, &output, dev_id, &srtp_crypto, &srtp_key); + pcap2wav(&args); cleanup(); return 0; From 5dc6604936b2f7093015f500ff7c2f765abe29f4 Mon Sep 17 00:00:00 2001 From: Florian Xaver Date: Thu, 22 Aug 2024 01:20:42 +0200 Subject: [PATCH 060/491] Use public address for outgoing responses' contact header (#4048) --- pjsip/src/pjsua-lib/pjsua_acc.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index 7c2d6c8b7e..a46336f2e1 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -3923,7 +3923,7 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uas_contact( pj_pool_t *pool, pjsua_init_tpselector(acc_id, &tp_sel); } - /* Get local address suitable to send request from */ + /* Get local address suitable to send response from */ pjsip_tpmgr_fla2_param_default(&tfla2_prm); tfla2_prm.tp_type = tp_type; tfla2_prm.tp_sel = &tp_sel; @@ -3940,6 +3940,26 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uas_contact( pj_pool_t *pool, local_addr = tfla2_prm.ret_addr; local_port = tfla2_prm.ret_port; + /* For UDP transport, check if we need to overwrite the address + * with its configured bound/public address. + */ + if ((flag & PJSIP_TRANSPORT_DATAGRAM) && tfla2_prm.local_if && + tfla2_prm.ret_tp) + { + int i; + + for (i = 0; i < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata); i++) { + if (tfla2_prm.ret_tp==(const void *)pjsua_var.tpdata[i].data.tp) { + if (pjsua_var.tpdata[i].has_cfg_addr) { + pj_strdup(pool, &local_addr, + &pjsua_var.tpdata[i].data.tp->local_name.host); + local_port = (pj_uint16_t) + pjsua_var.tpdata[i].data.tp->local_name.port; + } + break; + } + } + } /* Enclose IPv6 address in square brackets */ if (tp_type & PJSIP_TRANSPORT_IPV6) { From 865a5140f0afa927414e387cad623c7d8b807206 Mon Sep 17 00:00:00 2001 From: sauwming Date: Mon, 26 Aug 2024 13:35:04 +0800 Subject: [PATCH 061/491] Update dialog remote info in target refresh (#4049) --- pjsip/src/pjsip/sip_dialog.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c index a485a0dffe..eb0f537d26 100644 --- a/pjsip/src/pjsip/sip_dialog.c +++ b/pjsip/src/pjsip/sip_dialog.c @@ -1814,9 +1814,35 @@ void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata ) dlg->remote.contact->uri, contact->uri))) { + pj_str_t tmp; + enum { TMP_LEN=PJSIP_MAX_URL_SIZE }; + pj_ssize_t len; + + PJ_LOG(4, (dlg->obj_name, "Updating remote contact in " + "target refresh")); + dlg->remote.contact = (pjsip_contact_hdr*) pjsip_hdr_clone(dlg->pool, contact); dlg->target = dlg->remote.contact->uri; + + /* Update remote info as well. */ + dlg->remote.info = (pjsip_fromto_hdr*) + pjsip_hdr_clone(dlg->pool, rdata->msg_info.from); + pjsip_fromto_hdr_set_to(dlg->remote.info); + + /* Print the remote info. */ + tmp.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool, TMP_LEN); + len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, + dlg->remote.info->uri, tmp.ptr, TMP_LEN); + if (len < 1) { + tmp.slen=pj_ansi_strxcpy(tmp.ptr, "<-error: uri too long->", TMP_LEN); + if (tmp.slen<0) + tmp.slen = pj_ansi_strlen(tmp.ptr); + } else + tmp.slen = len; + + /* Save the remote info. */ + pj_strdup(dlg->pool, &dlg->remote.info_str, &tmp); } } From aa56436747313a3065754286907978b7a9d52efb Mon Sep 17 00:00:00 2001 From: Austin K Date: Mon, 26 Aug 2024 03:07:36 -0700 Subject: [PATCH 062/491] Allow user defined Call-ID to be sent on outgoing INVITE (#4052) --- pjsip/include/pjsua-lib/pjsua.h | 15 +++++++++++++++ pjsip/include/pjsua2/call.hpp | 13 +++++++++++++ pjsip/src/pjsua-lib/pjsua_call.c | 10 ++++++++++ pjsip/src/pjsua2/call.cpp | 6 ++++++ 4 files changed, 44 insertions(+) diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index a0bdf7d327..0fe6260873 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -1092,6 +1092,21 @@ typedef struct pjsua_call_setting */ pjmedia_dir media_dir[PJMEDIA_MAX_SDP_MEDIA]; + /** + * Custom Call-ID to be sent out with outgoing INVITE. Overrides + * default Call-ID generated by dialog. + * + * Note: It is up to the developer to verify uniqueness of the + * Call-ID as there will be no verification. The developer must + * change the Call-ID between calls creating a unique id for each + * outgoing call. + * + * This setting will only be used when creating a new outgoing call + * via pjsua_call_make_call(). + */ + + pj_str_t custom_call_id; + } pjsua_call_setting; diff --git a/pjsip/include/pjsua2/call.hpp b/pjsip/include/pjsua2/call.hpp index f14e78c678..743da677ac 100644 --- a/pjsip/include/pjsua2/call.hpp +++ b/pjsip/include/pjsua2/call.hpp @@ -333,6 +333,19 @@ struct CallSetting */ MediaDirVector mediaDir; + /** + * User defined Call-ID to be sent out with outgoing INVITE. + * + * Note: It is up to the developer to verify uniqueness of the + * Call-ID as there will be no verification. The developer must + * change the Call-ID between calls creating a unique id for each + * outgoing call. + * + * This setting will only be used when creating a new outgoing call + * via Call::makeCall(). + */ + string customCallId; + public: /** diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 9bd7ca434f..4dea22440c 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -954,6 +954,16 @@ PJ_DEF(pj_status_t) pjsua_call_make_call(pjsua_acc_id acc_id, goto on_error; } + /* + * Use pre defined Call-ID to be sent out with INVITE as opposed + * to using a randomly generated Call-ID + */ + + if( opt->custom_call_id.slen > 0 ){ + pj_strdup(dlg->pool, &dlg->call_id->id, &opt->custom_call_id); + PJ_LOG(4,(THIS_FILE, "Set user defined Call-ID (%.*s)", (int)dlg->call_id->id.slen, dlg->call_id->id.ptr )); + } + /* Increment the dialog's lock otherwise when invite session creation * fails the dialog will be destroyed prematurely. */ diff --git a/pjsip/src/pjsua2/call.cpp b/pjsip/src/pjsua2/call.cpp index 4f9b2bc375..c56a55abc0 100644 --- a/pjsip/src/pjsua2/call.cpp +++ b/pjsip/src/pjsua2/call.cpp @@ -234,6 +234,8 @@ void CallSetting::fromPj(const pjsua_call_setting &prm) this->audioCount = prm.aud_cnt; this->videoCount = prm.vid_cnt; this->mediaDir.clear(); + this->customCallId = pj2Str(prm.custom_call_id); + /* Since we don't know the size of media_dir array, we populate * mediaDir vector up to the element with non-default value. */ @@ -260,6 +262,10 @@ pjsua_call_setting CallSetting::toPj() const for (mi = 0; mi < this->mediaDir.size(); mi++) { setting.media_dir[mi] = (pjmedia_dir)this->mediaDir[mi]; } + + if( ! this->customCallId.empty()) { + setting.custom_call_id = str2Pj(this->customCallId); + } return setting; } From 8c2753f47af9f8c07b4f10701163e87640a2a3cb Mon Sep 17 00:00:00 2001 From: Austin K Date: Sun, 1 Sep 2024 18:56:49 -0700 Subject: [PATCH 063/491] PJSUA2 Crash Fix with CallSetting::fromPj (#4056) --- pjsip/src/pjsua-lib/pjsua_call.c | 6 +++++- pjsip/src/pjsua2/call.cpp | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 4dea22440c..0ee8fbdd2c 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -961,7 +961,11 @@ PJ_DEF(pj_status_t) pjsua_call_make_call(pjsua_acc_id acc_id, if( opt->custom_call_id.slen > 0 ){ pj_strdup(dlg->pool, &dlg->call_id->id, &opt->custom_call_id); - PJ_LOG(4,(THIS_FILE, "Set user defined Call-ID (%.*s)", (int)dlg->call_id->id.slen, dlg->call_id->id.ptr )); + PJ_LOG(4,(THIS_FILE, "Set user defined " + "Call-ID (%.*s)", + (int)dlg->call_id->id.slen, dlg->call_id->id.ptr )); + pj_bzero(&call->opt.custom_call_id, + sizeof(call->opt.custom_call_id)); } /* Increment the dialog's lock otherwise when invite session creation diff --git a/pjsip/src/pjsua2/call.cpp b/pjsip/src/pjsua2/call.cpp index c56a55abc0..cc23b27796 100644 --- a/pjsip/src/pjsua2/call.cpp +++ b/pjsip/src/pjsua2/call.cpp @@ -234,7 +234,7 @@ void CallSetting::fromPj(const pjsua_call_setting &prm) this->audioCount = prm.aud_cnt; this->videoCount = prm.vid_cnt; this->mediaDir.clear(); - this->customCallId = pj2Str(prm.custom_call_id); + this->customCallId = pj2Str(prm.custom_call_id); /* Since we don't know the size of media_dir array, we populate * mediaDir vector up to the element with non-default value. From 22b78f901a42fc5ac4120de764a17e8a832e220e Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Mon, 2 Sep 2024 14:37:25 +0700 Subject: [PATCH 064/491] Comment out unimplemented pjsua_transport_set_enable() (#4060) --- pjmedia/src/pjmedia-codec/silk.c | 2 +- pjsip/include/pjsua-lib/pjsua.h | 3 +++ pjsip/include/pjsua2/endpoint.hpp | 3 +++ pjsip/src/pjsua-lib/pjsua_core.c | 2 ++ pjsip/src/pjsua2/endpoint.cpp | 3 +++ 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/pjmedia/src/pjmedia-codec/silk.c b/pjmedia/src/pjmedia-codec/silk.c index 11c07eb19e..5347a11e92 100644 --- a/pjmedia/src/pjmedia-codec/silk.c +++ b/pjmedia/src/pjmedia-codec/silk.c @@ -700,7 +700,7 @@ static pj_status_t silk_codec_modify(pjmedia_codec *codec, PJ_UNUSED_ARG(codec); PJ_UNUSED_ARG(attr); - return PJ_SUCCESS; + return PJ_ENOTSUP; } diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 0fe6260873..d0951b3cf5 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -3410,6 +3410,8 @@ PJ_DECL(pj_status_t) pjsua_transport_get_info(pjsua_transport_id id, pjsua_transport_info *info); +#if 0 +// Not implemented. /** * Disable a transport or re-enable it. By default transport is always * enabled after it is created. Disabling a transport does not necessarily @@ -3423,6 +3425,7 @@ PJ_DECL(pj_status_t) pjsua_transport_get_info(pjsua_transport_id id, */ PJ_DECL(pj_status_t) pjsua_transport_set_enable(pjsua_transport_id id, pj_bool_t enabled); +#endif /** diff --git a/pjsip/include/pjsua2/endpoint.hpp b/pjsip/include/pjsua2/endpoint.hpp index 5c78de026e..c446d04081 100644 --- a/pjsip/include/pjsua2/endpoint.hpp +++ b/pjsip/include/pjsua2/endpoint.hpp @@ -1533,6 +1533,8 @@ class Endpoint */ TransportInfo transportGetInfo(TransportId id) const PJSUA2_THROW(Error); +#if 0 + // Not implemented. /** * Disable a transport or re-enable it. By default transport is always * enabled after it is created. Disabling a transport does not necessarily @@ -1544,6 +1546,7 @@ class Endpoint * */ void transportSetEnable(TransportId id, bool enabled) PJSUA2_THROW(Error); +#endif /** * Close the transport. The system will wait until all transactions are diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 10fe57d9b5..4cd94bd051 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -2937,6 +2937,7 @@ PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id, } +#if 0 /* * Disable a transport or re-enable it. */ @@ -2957,6 +2958,7 @@ PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id, return PJ_EINVALIDOP; } +#endif /* diff --git a/pjsip/src/pjsua2/endpoint.cpp b/pjsip/src/pjsua2/endpoint.cpp index 487b095166..b6fc1aed6d 100644 --- a/pjsip/src/pjsua2/endpoint.cpp +++ b/pjsip/src/pjsua2/endpoint.cpp @@ -2287,11 +2287,14 @@ TransportInfo Endpoint::transportGetInfo(TransportId id) const PJSUA2_THROW(Erro return tinfo; } +#if 0 +// pjsua_transport_set_enable() not implemented void Endpoint::transportSetEnable(TransportId id, bool enabled) PJSUA2_THROW(Error) { PJSUA2_CHECK_EXPR( pjsua_transport_set_enable(id, enabled) ); } +#endif void Endpoint::transportClose(TransportId id) PJSUA2_THROW(Error) { From d555ec102be0a6841914202116d399042f7a6e80 Mon Sep 17 00:00:00 2001 From: Riza Sulistyo Date: Tue, 3 Sep 2024 09:46:38 +0700 Subject: [PATCH 065/491] Use setuptools to replace distutils (#4057) --- pjsip-apps/src/swig/python/setup.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pjsip-apps/src/swig/python/setup.py b/pjsip-apps/src/swig/python/setup.py index c49cd64e4e..18f802a8f4 100644 --- a/pjsip-apps/src/swig/python/setup.py +++ b/pjsip-apps/src/swig/python/setup.py @@ -17,7 +17,11 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -from distutils.core import setup, Extension +try: + from setuptools import setup, Extension +except ImportError: + from distutils.core import setup, Extension + import os import sys import platform From cbccd497a9f6cf1e6bf7b72a92980e40cdbc7694 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Mon, 9 Sep 2024 10:14:16 +0700 Subject: [PATCH 066/491] Avoid dialog destroy while waiting for dialog lock in sending SIP message. (#4064) --- pjsip/src/pjsip-ua/sip_inv.c | 38 ++++++++++++++++++++++++++++++++---- pjsip/src/pjsip/sip_dialog.c | 8 ++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c index c157bb1ecf..73197abc29 100644 --- a/pjsip/src/pjsip-ua/sip_inv.c +++ b/pjsip/src/pjsip-ua/sip_inv.c @@ -1926,6 +1926,8 @@ PJ_DEF(pj_status_t) pjsip_inv_uac_restart(pjsip_inv_session *inv, { PJ_ASSERT_RETURN(inv, PJ_EINVAL); + pjsip_inv_add_ref(inv); + inv->state = PJSIP_INV_STATE_NULL; inv->invite_tsx = NULL; if (inv->last_answer) { @@ -1942,6 +1944,8 @@ PJ_DEF(pj_status_t) pjsip_inv_uac_restart(pjsip_inv_session *inv, } } + pjsip_inv_dec_ref(inv); + return PJ_SUCCESS; } @@ -2814,6 +2818,8 @@ PJ_DEF(pj_status_t) pjsip_inv_set_local_sdp(pjsip_inv_session *inv, PJ_ASSERT_RETURN(inv && sdp, PJ_EINVAL); + pjsip_inv_add_ref(inv); + /* If we have remote SDP offer, set local answer to respond to the offer, * otherwise we set/modify our local offer (and create an SDP negotiator * if we don't have one yet). @@ -2830,13 +2836,17 @@ PJ_DEF(pj_status_t) pjsip_inv_set_local_sdp(pjsip_inv_session *inv, status = pjmedia_sdp_neg_modify_local_offer2(inv->pool, inv->neg, inv->sdp_neg_flags, sdp); - } else + } else { + pjsip_inv_dec_ref(inv); return PJMEDIA_SDPNEG_EINSTATE; + } } else { status = pjmedia_sdp_neg_create_w_local_offer(inv->pool, sdp, &inv->neg); } + pjsip_inv_dec_ref(inv); + return status; } @@ -2874,6 +2884,7 @@ PJ_DEF(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv, PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL); pj_log_push_indent(); + pjsip_inv_add_ref(inv); /* Set cause code. */ inv_set_cause(inv, st_code, st_text); @@ -2890,7 +2901,8 @@ PJ_DEF(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv, /* For UAC when session has not been confirmed, create CANCEL. */ /* MUST have the original UAC INVITE transaction. */ - PJ_ASSERT_RETURN(inv->invite_tsx != NULL, PJ_EBUG); + PJ_ASSERT_ON_FAIL(inv->invite_tsx != NULL, + { pjsip_inv_dec_ref(inv); return PJ_EBUG; }); /* But CANCEL should only be called when we have received a * provisional response. If we haven't received any responses, @@ -2905,6 +2917,7 @@ PJ_DEF(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv, *p_tdata = NULL; PJ_LOG(4, (inv->obj_name, "Delaying CANCEL since no " "provisional response is received yet")); + pjsip_inv_dec_ref(inv); pj_log_pop_indent(); return PJ_SUCCESS; } @@ -2918,6 +2931,7 @@ PJ_DEF(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv, inv->invite_tsx->last_tx, &tdata); if (status != PJ_SUCCESS) { + pjsip_inv_dec_ref(inv); pj_log_pop_indent(); return status; } @@ -2938,7 +2952,9 @@ PJ_DEF(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv, if (tdata == NULL) tdata = inv->last_answer; - PJ_ASSERT_RETURN(tdata != NULL, PJ_EINVALIDOP); + PJ_ASSERT_ON_FAIL(tdata != NULL, + { pjsip_inv_dec_ref(inv); + return PJ_EINVALIDOP; }); //status = pjsip_dlg_modify_response(inv->dlg, tdata, st_code, // st_text); @@ -2960,16 +2976,19 @@ PJ_DEF(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv, case PJSIP_INV_STATE_DISCONNECTED: /* No need to do anything. */ + pjsip_inv_dec_ref(inv); pj_log_pop_indent(); return PJSIP_ESESSIONTERMINATED; default: pj_assert(!"Invalid operation!"); + pjsip_inv_dec_ref(inv); pj_log_pop_indent(); return PJ_EINVALIDOP; } if (status != PJ_SUCCESS) { + pjsip_inv_dec_ref(inv); pj_log_pop_indent(); return status; } @@ -2980,6 +2999,7 @@ PJ_DEF(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv, inv->cancelling = PJ_TRUE; *p_tdata = tdata; + pjsip_inv_dec_ref(inv); pj_log_pop_indent(); return PJ_SUCCESS; } @@ -2997,6 +3017,7 @@ PJ_DEF(pj_status_t) pjsip_inv_cancel_reinvite( pjsip_inv_session *inv, PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL); pj_log_push_indent(); + pjsip_inv_add_ref(inv); /* Create appropriate message. */ switch (inv->state) { @@ -3013,6 +3034,7 @@ PJ_DEF(pj_status_t) pjsip_inv_cancel_reinvite( pjsip_inv_session *inv, *p_tdata = NULL; PJ_LOG(4, (inv->obj_name, "Delaying CANCEL since no " "provisional response is received yet")); + pjsip_inv_dec_ref(inv); pj_log_pop_indent(); return PJ_SUCCESS; } @@ -3021,6 +3043,7 @@ PJ_DEF(pj_status_t) pjsip_inv_cancel_reinvite( pjsip_inv_session *inv, inv->invite_tsx->last_tx, &tdata); if (status != PJ_SUCCESS) { + pjsip_inv_dec_ref(inv); pj_log_pop_indent(); return status; } @@ -3030,10 +3053,12 @@ PJ_DEF(pj_status_t) pjsip_inv_cancel_reinvite( pjsip_inv_session *inv, /* We cannot send CANCEL to a re-INVITE if the INVITE session is * not confirmed. */ + pjsip_inv_dec_ref(inv); pj_log_pop_indent(); return PJ_EINVALIDOP; } + pjsip_inv_dec_ref(inv); pj_log_pop_indent(); *p_tdata = tdata; @@ -3732,6 +3757,7 @@ PJ_DEF(pj_status_t) pjsip_inv_send_msg( pjsip_inv_session *inv, PJ_ASSERT_RETURN(inv && tdata, PJ_EINVAL); pj_log_push_indent(); + pjsip_inv_add_ref(inv); PJ_LOG(5,(inv->obj_name, "Sending %s", pjsip_tx_data_get_info(tdata))); @@ -3819,7 +3845,9 @@ PJ_DEF(pj_status_t) pjsip_inv_send_msg( pjsip_inv_session *inv, cseq = (pjsip_cseq_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); PJ_ASSERT_ON_FAIL(cseq != NULL && (inv->invite_tsx && cseq->cseq == inv->invite_tsx->cseq), - { pjsip_tx_data_dec_ref(tdata); return PJ_EINVALIDOP; }); + { pjsip_tx_data_dec_ref(tdata); + pjsip_inv_dec_ref(inv); + return PJ_EINVALIDOP; }); if (inv->options & PJSIP_INV_REQUIRE_100REL) { status = pjsip_100rel_tx_response(inv, tdata); @@ -3835,10 +3863,12 @@ PJ_DEF(pj_status_t) pjsip_inv_send_msg( pjsip_inv_session *inv, /* Done */ on_return: + pjsip_inv_dec_ref(inv); pj_log_pop_indent(); return PJ_SUCCESS; on_error: + pjsip_inv_dec_ref(inv); pj_log_pop_indent(); return status; } diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c index eb0f537d26..7df463d529 100644 --- a/pjsip/src/pjsip/sip_dialog.c +++ b/pjsip/src/pjsip/sip_dialog.c @@ -948,6 +948,11 @@ PJ_DEF(pj_status_t) pjsip_dlg_inc_session( pjsip_dialog *dlg, */ PJ_DEF(void) pjsip_dlg_inc_lock(pjsip_dialog *dlg) { + /* Add ref temporarily to avoid possible dialog destroy while waiting + * the lock. + */ + pj_grp_lock_add_ref(dlg->grp_lock_); + PJ_LOG(6,(dlg->obj_name, "Entering pjsip_dlg_inc_lock(), sess_count=%d", dlg->sess_count)); @@ -956,6 +961,9 @@ PJ_DEF(void) pjsip_dlg_inc_lock(pjsip_dialog *dlg) PJ_LOG(6,(dlg->obj_name, "Leaving pjsip_dlg_inc_lock(), sess_count=%d", dlg->sess_count)); + + /* Lock has been acquired, dec ref */ + pj_grp_lock_dec_ref(dlg->grp_lock_); } /* Try to acquire dialog's group lock, but bail out if group lock can not be From a6ed899e22f5b151ff9d07a21570c5f19ef6457a Mon Sep 17 00:00:00 2001 From: "YAMASHIRO, Jun" Date: Wed, 11 Sep 2024 12:58:18 +0900 Subject: [PATCH 067/491] Fix build error with Opus and Visual Studio (#4058) --- pjmedia/src/pjmedia-codec/opus.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pjmedia/src/pjmedia-codec/opus.c b/pjmedia/src/pjmedia-codec/opus.c index 1264a41ec2..5446fbff60 100644 --- a/pjmedia/src/pjmedia-codec/opus.c +++ b/pjmedia/src/pjmedia-codec/opus.c @@ -1244,8 +1244,11 @@ static pj_status_t codec_recover( pjmedia_codec *codec, } #if defined(_MSC_VER) -# pragma comment(lib, "libopus.a") +# if 1 /* Change to 0 if Opus lib name is "opus.lib" */ +# pragma comment(lib, "libopus.a") +# else +# pragma comment(lib, "opus.lib") +# endif #endif - #endif /* PJMEDIA_HAS_OPUS_CODEC */ From 9b6744f3999b7bd908ef209faab8a4a30967813c Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Wed, 11 Sep 2024 13:12:49 +0700 Subject: [PATCH 068/491] Add missing PJLIB dir in SWIG include dir search in VS2005 SWIG Java project (#4070) --- pjsip-apps/build/swig_java_pjsua2.vcproj | 8 ++++---- pjsip-apps/src/swig/java/sample.java | 12 +++++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/pjsip-apps/build/swig_java_pjsua2.vcproj b/pjsip-apps/build/swig_java_pjsua2.vcproj index 255f558467..20111da9a9 100644 --- a/pjsip-apps/build/swig_java_pjsua2.vcproj +++ b/pjsip-apps/build/swig_java_pjsua2.vcproj @@ -357,7 +357,7 @@ > @@ -366,7 +366,7 @@ > @@ -375,7 +375,7 @@ > @@ -384,7 +384,7 @@ > diff --git a/pjsip-apps/src/swig/java/sample.java b/pjsip-apps/src/swig/java/sample.java index dabf8d218c..a50c97ef3b 100644 --- a/pjsip-apps/src/swig/java/sample.java +++ b/pjsip-apps/src/swig/java/sample.java @@ -105,7 +105,8 @@ public class sample { private static MyApp app = new MyApp(); private static MyObserver observer = new MyObserver(); private static MyAccount account = null; - private static AccountConfig accCfg = null; + private static AccountConfig accCfg = null; + private static MyCall call = null; // Snippet code to set native window to output video /* @@ -156,6 +157,15 @@ private static void runWorker() { try { account.modify(accCfg); + + /* Make call to self */ + /* + call = new MyCall(app.accList.get(0), -1); + CallOpParam prm = new CallOpParam(true); + prm.getOpt().setAudioCount(1); + prm.getOpt().setVideoCount(0); + call.makeCall("sip:localhost:6000", prm); + */ } catch (Exception e) {} while (!Thread.currentThread().isInterrupted()) { From f67e3ed240fc741cd56cff20f8f08ebb8cdd5203 Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 11 Sep 2024 15:59:10 +0800 Subject: [PATCH 069/491] Fixed length check for RTCP FB SLI and RPSI parsing (#4069) --- pjmedia/src/pjmedia/rtcp_fb.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pjmedia/src/pjmedia/rtcp_fb.c b/pjmedia/src/pjmedia/rtcp_fb.c index 15c4668be1..ecb243ed61 100644 --- a/pjmedia/src/pjmedia/rtcp_fb.c +++ b/pjmedia/src/pjmedia/rtcp_fb.c @@ -704,7 +704,8 @@ PJ_DEF(pj_status_t) pjmedia_rtcp_fb_parse_sli( return PJ_ETOOSMALL; } - cnt = pj_ntohs((pj_uint16_t)hdr->rtcp_common.length) - 2; + cnt = pj_ntohs((pj_uint16_t)hdr->rtcp_common.length); + if (cnt > 2) cnt -= 2; else cnt = 0; if (length < (cnt+3)*4) return PJ_ETOOSMALL; @@ -755,7 +756,9 @@ PJ_DEF(pj_status_t) pjmedia_rtcp_fb_parse_rpsi( return PJ_ETOOSMALL; } - rpsi_len = (pj_ntohs((pj_uint16_t)hdr->rtcp_common.length)-2) * 4; + rpsi_len = pj_ntohs((pj_uint16_t)hdr->rtcp_common.length); + if (rpsi_len > 2) rpsi_len -= 2; else rpsi_len = 0; + rpsi_len *= 4; if (length < rpsi_len + 12) return PJ_ETOOSMALL; From a85f793ab44f16d534c8c9a9b192fdcf2f3b833c Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 11 Sep 2024 16:00:17 +0800 Subject: [PATCH 070/491] Add support for Android flexible page sizes (#4068) --- pjsip-apps/src/samples/android_sample/jni/Application.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pjsip-apps/src/samples/android_sample/jni/Application.mk b/pjsip-apps/src/samples/android_sample/jni/Application.mk index 8c58da6fff..fc83bdd806 100644 --- a/pjsip-apps/src/samples/android_sample/jni/Application.mk +++ b/pjsip-apps/src/samples/android_sample/jni/Application.mk @@ -1,3 +1,5 @@ APP_ABI := all APP_CPPFLAGS := -fexceptions -frtti APP_STL := c++_shared + +APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true From 194dec37248d242a231b80cab5e19a24c9644618 Mon Sep 17 00:00:00 2001 From: sauwming Date: Thu, 12 Sep 2024 14:53:16 +0800 Subject: [PATCH 071/491] Update cifuzz.yml-upload-artifact@v1 is deprecated (#4072) --- .github/workflows/cifuzz.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index 8ec8ba48b7..c739b93b34 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -25,7 +25,7 @@ jobs: fuzz-seconds: 300 sanitizer: ${{ matrix.sanitizer }} - name: Upload Crash - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 if: failure() && steps.build.outcome == 'success' with: name: ${{ matrix.sanitizer }}-artifacts From 3c3eef95786c17b6c49c51da9ddbeeee76607f32 Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 13 Sep 2024 13:18:47 +0800 Subject: [PATCH 072/491] Add socket option params to transport config in PJSUA2 (#4071) --- pjlib/include/pj/sock.h | 13 +++ pjlib/src/pj/sock_bsd.c | 11 ++ pjlib/src/pj/sock_common.c | 24 ++++ pjlib/src/pj/ssl_sock_common.c | 2 + .../main/java/org/pjsip/pjsua2/app/MyApp.java | 19 ++++ pjsip-apps/src/swig/pjsua2.i | 1 + pjsip/include/pjsip/sip_transport_tls.h | 2 + pjsip/include/pjsua2/siptypes.hpp | 106 +++++++++++++++++ pjsip/src/pjsip/sip_transport_tcp.c | 4 +- pjsip/src/pjsua2/json.cpp | 8 +- pjsip/src/pjsua2/siptypes.cpp | 107 ++++++++++++++++++ 11 files changed, 293 insertions(+), 4 deletions(-) diff --git a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h index 2009216cb7..88d679c5e6 100644 --- a/pjlib/include/pj/sock.h +++ b/pjlib/include/pj/sock.h @@ -1517,6 +1517,19 @@ PJ_DECL(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd, ***************************************************************************** */ +/** + * Deep clone the socket options. + * + * @param pool The pool. + * @param dst Destination socket options. + * @param src Source socket options. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pj_sockopt_params_clone(pj_pool_t *pool, + pj_sockopt_params *dst, + const pj_sockopt_params *src); + /** * Print socket address string. This method will enclose the address string * with square bracket if it's IPv6 address. diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c index 035eb6c279..5f594efa74 100644 --- a/pjlib/src/pj/sock_bsd.c +++ b/pjlib/src/pj/sock_bsd.c @@ -25,6 +25,14 @@ #include #include +#if 0 + /* Enable some tracing */ + #include + #define TRACE_(arg) PJ_LOG(4,arg) +#else + #define TRACE_(arg) +#endif + #define THIS_FILE "sock_bsd.c" /* @@ -569,6 +577,7 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, #endif *sock = socket(af, type, proto); + TRACE_((THIS_FILE, "Created new socket of type %d: %ld", type, *sock)); if (*sock == PJ_INVALID_SOCKET) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else { @@ -832,6 +841,8 @@ PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock, (const char*)optval, optlen); #else status = setsockopt(sock, level, optname, (const char*)optval, optlen); + TRACE_((THIS_FILE, "setsockopt %ld level:%d name:%d val:%d(%d)->%d", sock, + level, optname, *((const char *)optval), optlen, status)); #endif if (status != 0) diff --git a/pjlib/src/pj/sock_common.c b/pjlib/src/pj/sock_common.c index 23f859707a..5c8d8a63dd 100644 --- a/pjlib/src/pj/sock_common.c +++ b/pjlib/src/pj/sock_common.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1205,6 +1206,29 @@ PJ_DEF(pj_status_t) pj_sock_setsockopt_sobuf( pj_sock_t sockfd, } +PJ_DEF(pj_status_t) pj_sockopt_params_clone(pj_pool_t *pool, + pj_sockopt_params *dst, + const pj_sockopt_params *src) +{ + unsigned int i; + + PJ_ASSERT_RETURN(pool && src && dst, PJ_EINVAL); + + pj_memcpy(dst, src, sizeof(pj_sockopt_params)); + for (i = 0; i < dst->cnt && i < PJ_MAX_SOCKOPT_PARAMS; ++i) { + if (dst->options[i].optlen == 0) { + dst->options[i].optval = NULL; + continue; + } + dst->options[i].optval = pj_pool_alloc(pool, dst->options[i].optlen); + pj_memcpy(dst->options[i].optval, src->options[i].optval, + dst->options[i].optlen); + } + + return PJ_SUCCESS; +} + + PJ_DEF(char *) pj_addr_str_print( const pj_str_t *host_str, int port, char *buf, int size, unsigned flag) { diff --git a/pjlib/src/pj/ssl_sock_common.c b/pjlib/src/pj/ssl_sock_common.c index 65aba8ddab..4632ce079f 100644 --- a/pjlib/src/pj/ssl_sock_common.c +++ b/pjlib/src/pj/ssl_sock_common.c @@ -107,6 +107,8 @@ PJ_DEF(void) pj_ssl_sock_param_copy( pj_pool_t *pool, /* Path name must be null-terminated */ pj_strdup_with_null(pool, &dst->entropy_path, &src->entropy_path); } + + pj_sockopt_params_clone(pool, &dst->sockopt_params, &src->sockopt_params); } diff --git a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java index 1ebaeda8d7..adb994d7b9 100644 --- a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java +++ b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java @@ -416,6 +416,25 @@ public void init(MyAppObserver obs, String app_dir, } try { + /* Setting socket option parameters (uncomment this if needed). */ + /* + final int SOL_SOCKET = 1; + final int SOL_TCP = 6; + + final int SO_KEEPALIVE = 9; + final int TCP_KEEPIDLE = 4; + final int TCP_KEEPINTVL = 5; + final int TCP_KEEPCNT = 6; + + SockOptVector soVector = new SockOptVector(); + soVector.add(new SockOpt(SOL_SOCKET, SO_KEEPALIVE, 1)); + soVector.add(new SockOpt(SOL_TCP, TCP_KEEPIDLE, 1)); + soVector.add(new SockOpt(SOL_TCP, TCP_KEEPINTVL, 5)); + soVector.add(new SockOpt(SOL_TCP, TCP_KEEPCNT, 1)); + + sipTpConfig.getTlsConfig().getSockOptParams().setSockOpts(soVector); + */ + sipTpConfig.setPort(SIP_PORT+1); ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_TLS, sipTpConfig); diff --git a/pjsip-apps/src/swig/pjsua2.i b/pjsip-apps/src/swig/pjsua2.i index 0aba16c0f0..5c7de25a79 100644 --- a/pjsip-apps/src/swig/pjsua2.i +++ b/pjsip-apps/src/swig/pjsua2.i @@ -166,6 +166,7 @@ using namespace pj; %include "pjsua2/siptypes.hpp" +%template(SockOptVector) std::vector; %template(SipHeaderVector) std::vector; %template(AuthCredInfoVector) std::vector; %template(SrtpCryptoVector) std::vector; diff --git a/pjsip/include/pjsip/sip_transport_tls.h b/pjsip/include/pjsip/sip_transport_tls.h index ce9aab95ca..8c0d014f35 100644 --- a/pjsip/include/pjsip/sip_transport_tls.h +++ b/pjsip/include/pjsip/sip_transport_tls.h @@ -500,6 +500,8 @@ PJ_INLINE(void) pjsip_tls_setting_copy(pj_pool_t *pool, for (i=0; icurves_num; ++i) dst->curves[i] = src->curves[i]; } + + pj_sockopt_params_clone(pool, &dst->sockopt_params, &src->sockopt_params); } diff --git a/pjsip/include/pjsua2/siptypes.hpp b/pjsip/include/pjsua2/siptypes.hpp index 7018f404e1..9705bcc711 100644 --- a/pjsip/include/pjsua2/siptypes.hpp +++ b/pjsip/include/pjsua2/siptypes.hpp @@ -126,6 +126,86 @@ struct AuthCredInfo : public PersistentObject ////////////////////////////////////////////////////////////////////////////// +/** + * Socket option type. + */ +struct SockOpt { + /** + * The level at which the option is defined. + */ + int level; + + /** + * Option name. + */ + int optName; + +public: + /** Default constructor. */ + SockOpt(); + + /** Construct a socket option with the specified parameters. */ + SockOpt(int level, int optName, int optVal); + + /** + * Set option value of type integer. + * + * @param opt_val Option value. + */ + void setOptValInt(int opt_val); + +private: + friend struct SockOptParams; + + /** Pointer to the buffer in which the option is specified. */ + void *optVal; + + /** Buffer size of the buffer pointed by optVal. */ + int optLen; + + /** Option value if the type is integer. */ + int optValInt; +}; + +/** Array of socket options */ +typedef std::vector SockOptVector; + +/** + * Socket option parameters, to be specified in TransportConfig. + */ +struct SockOptParams : public PersistentObject +{ + /** + * Array of socket options. + */ + SockOptVector sockOpts; + +public: + /** Default constructor initialises with default values */ + SockOptParams(); + + /** Convert to pjsip */ + pj_sockopt_params toPj() const; + + /** Convert from pjsip */ + void fromPj(const pj_sockopt_params &prm); + + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) PJSUA2_THROW(Error); + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const PJSUA2_THROW(Error); +}; + + /** * TLS transport settings, to be specified in TransportConfig. */ @@ -300,6 +380,22 @@ struct TlsConfig : public PersistentObject */ bool qosIgnoreError; + /** + * Specify options to be set on the transport. + * + * By default, this is unset, which means that the underlying sockopt + * params as returned by #pj_ssl_sock_param_default() will be used. + */ + SockOptParams sockOptParams; + + /** + * Specify if the transport should ignore any errors when setting the + * sockopt parameters. + * + * Default: true + */ + bool sockOptIgnoreError; + /** * Specify if renegotiation is enabled for TLSv1.2 or earlier. * @@ -424,6 +520,16 @@ struct TransportConfig : public PersistentObject */ pj_qos_params qosParams; + /** + * Set the low level socket options to the transport. + * + * For TLS transport, this field will be ignored, the socket options + * can be set via tlsConfig. + * + * Default is no socket option set. + */ + SockOptParams sockOptParams; + public: /** Default constructor initialises with default values */ TransportConfig(); diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c index 303a57ee9e..973ecfb0ea 100644 --- a/pjsip/src/pjsip/sip_transport_tcp.c +++ b/pjsip/src/pjsip/sip_transport_tcp.c @@ -401,8 +401,8 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start3( listener->initial_timeout = cfg->initial_timeout; pj_memcpy(&listener->qos_params, &cfg->qos_params, sizeof(cfg->qos_params)); - pj_memcpy(&listener->sockopt_params, &cfg->sockopt_params, - sizeof(cfg->sockopt_params)); + pj_sockopt_params_clone(pool, &listener->sockopt_params, + &cfg->sockopt_params); pj_ansi_strxcpy(listener->factory.obj_name, "tcptp", sizeof(listener->factory.obj_name)); diff --git a/pjsip/src/pjsua2/json.cpp b/pjsip/src/pjsua2/json.cpp index 253b98681d..a34c1550d9 100644 --- a/pjsip/src/pjsua2/json.cpp +++ b/pjsip/src/pjsua2/json.cpp @@ -473,7 +473,9 @@ static void jsonNode_writeString(ContainerNode *node, pj_json_elem *el = jdat->doc->allocElement(); pj_str_t nm = alloc_name(jdat->doc, name); pj_str_t new_val; - pj_strdup2(jdat->doc->getPool(), &new_val, value.c_str()); + pj_str_t str_val; + pj_strset(&str_val, (char*)value.data(), value.size()); + pj_strdup(jdat->doc->getPool(), &new_val, &str_val); pj_json_elem_string(el, &nm, &new_val); pj_json_elem_add(jdat->jnode, el); @@ -491,8 +493,10 @@ static void jsonNode_writeStringVector(ContainerNode *node, pj_json_elem_array(el, &nm); for (unsigned i=0; idoc->getPool(), &new_val, value[i].c_str()); + pj_strset(&str_val, (char*)value[i].data(), value[i].size()); + pj_strdup(jdat->doc->getPool(), &new_val, &str_val); pj_json_elem *child = jdat->doc->allocElement(); pj_json_elem_string(child, NULL, &new_val); pj_json_elem_add(el, child); diff --git a/pjsip/src/pjsua2/siptypes.cpp b/pjsip/src/pjsua2/siptypes.cpp index dd5716c5d4..6416964a46 100644 --- a/pjsip/src/pjsua2/siptypes.cpp +++ b/pjsip/src/pjsua2/siptypes.cpp @@ -209,6 +209,8 @@ pjsip_tls_setting TlsConfig::toPj() const ts.qos_type = this->qosType; ts.qos_params = this->qosParams; ts.qos_ignore_error = this->qosIgnoreError; + ts.sockopt_params = this->sockOptParams.toPj(); + ts.sockopt_ignore_error = this->sockOptIgnoreError; ts.enable_renegotiation = this->enableRenegotiation; return ts; @@ -237,6 +239,8 @@ void TlsConfig::fromPj(const pjsip_tls_setting &prm) this->qosType = prm.qos_type; this->qosParams = prm.qos_params; this->qosIgnoreError = PJ2BOOL(prm.qos_ignore_error); + this->sockOptParams.fromPj(prm.sockopt_params); + this->sockOptIgnoreError = PJ2BOOL(prm.sockopt_ignore_error); this->enableRenegotiation = PJ2BOOL(prm.enable_renegotiation); } @@ -260,6 +264,8 @@ void TlsConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error) NODE_READ_NUM_T ( this_node, pj_qos_type, qosType); readQosParams ( this_node, qosParams); NODE_READ_BOOL ( this_node, qosIgnoreError); + NODE_READ_OBJ ( this_node, sockOptParams); + NODE_READ_BOOL ( this_node, sockOptIgnoreError); NODE_READ_NUM_T ( this_node, pj_ssl_cert_lookup_type, certLookupType); NODE_READ_STRING ( this_node, certLookupKeyword); } @@ -284,12 +290,109 @@ void TlsConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error) NODE_WRITE_NUM_T ( this_node, pj_qos_type, qosType); writeQosParams ( this_node, qosParams); NODE_WRITE_BOOL ( this_node, qosIgnoreError); + NODE_WRITE_OBJ ( this_node, sockOptParams); + NODE_WRITE_BOOL ( this_node, sockOptIgnoreError); NODE_WRITE_NUM_T ( this_node, pj_ssl_cert_lookup_type, certLookupType); NODE_WRITE_STRING ( this_node, certLookupKeyword); } /////////////////////////////////////////////////////////////////////////////// +SockOpt::SockOpt() +{ + pj_bzero(this, sizeof(*this)); +} + +SockOpt::SockOpt(int level, int optName, int optVal) +{ + pj_bzero(this, sizeof(*this)); + this->level = level; + this->optName = optName; + setOptValInt(optVal); +} + +void SockOpt::setOptValInt(int opt_val) +{ + optVal = &optValInt; + optLen = sizeof(int); + optValInt = opt_val; +} + +SockOptParams::SockOptParams() +{ +} + +pj_sockopt_params SockOptParams::toPj() const +{ + pj_sockopt_params sop; + unsigned i; + + pj_bzero(&sop, sizeof(sop)); + sop.cnt = this->sockOpts.size(); + if (sop.cnt > PJ_MAX_SOCKOPT_PARAMS) + sop.cnt = PJ_MAX_SOCKOPT_PARAMS; + for (i = 0; i < sop.cnt; ++i) { + sop.options[i].level = this->sockOpts[i].level; + sop.options[i].optname = this->sockOpts[i].optName; + sop.options[i].optval = this->sockOpts[i].optVal; + sop.options[i].optlen = this->sockOpts[i].optLen; + } + + return sop; +} + +void SockOptParams::fromPj(const pj_sockopt_params &prm) +{ + unsigned i; + + this->sockOpts.clear(); + for (i = 0; i < prm.cnt; ++i) { + SockOpt so; + so.level = prm.options[i].level; + so.optName = prm.options[i].optname; + if (prm.options[i].optlen == sizeof(int)) { + so.setOptValInt(*((int *)prm.options[i].optval)); + } + this->sockOpts.push_back(so); + } +} + +void SockOptParams::readObject(const ContainerNode &node) PJSUA2_THROW(Error) +{ + ContainerNode array_node = node.readArray("sockOptParams"); + sockOpts.resize(0); + while (array_node.hasUnread()) { + ContainerNode so_node = array_node.readContainer("sockOpt"); + SockOpt so; + so.level = so_node.readInt("level"); + so.optName = so_node.readInt("optName"); + so.optLen = so_node.readInt("optLen"); + if (so.optLen == sizeof(int)) { + int optVal = so_node.readInt("optVal"); + so.setOptValInt(optVal); + } + sockOpts.push_back(so); + } +} + +void SockOptParams::writeObject(ContainerNode &node) const PJSUA2_THROW(Error) +{ + ContainerNode array_node = node.writeNewArray("sockOptParams"); + for (unsigned i=0; i 0? sockOpts[i].optLen : 0); + so_node.writeInt("level", sockOpts[i].level); + so_node.writeInt("optName", sockOpts[i].optName); + so_node.writeInt("optLen", sockOpts[i].optLen); + if (sockOpts[i].optLen == sizeof(int)) { + so_node.writeInt("optVal", sockOpts[i].optValInt); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + TransportConfig::TransportConfig() : qosType(PJ_QOS_TYPE_BEST_EFFORT) { pjsua_transport_config tc; @@ -307,6 +410,7 @@ void TransportConfig::fromPj(const pjsua_transport_config &prm) this->tlsConfig.fromPj(prm.tls_setting); this->qosType = prm.qos_type; this->qosParams = prm.qos_params; + this->sockOptParams.fromPj(prm.sockopt_params); } pjsua_transport_config TransportConfig::toPj() const @@ -322,6 +426,7 @@ pjsua_transport_config TransportConfig::toPj() const tc.tls_setting = this->tlsConfig.toPj(); tc.qos_type = this->qosType; tc.qos_params = this->qosParams; + tc.sockopt_params = this->sockOptParams.toPj(); return tc; } @@ -337,6 +442,7 @@ void TransportConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error) NODE_READ_NUM_T ( this_node, pj_qos_type, qosType); readQosParams ( this_node, qosParams); NODE_READ_OBJ ( this_node, tlsConfig); + NODE_READ_OBJ ( this_node, sockOptParams); } void TransportConfig::writeObject(ContainerNode &node) const @@ -351,6 +457,7 @@ void TransportConfig::writeObject(ContainerNode &node) const NODE_WRITE_NUM_T ( this_node, pj_qos_type, qosType); writeQosParams ( this_node, qosParams); NODE_WRITE_OBJ ( this_node, tlsConfig); + NODE_WRITE_OBJ ( this_node, sockOptParams); } /////////////////////////////////////////////////////////////////////////////// From 9543a1bcf50be721d030be99afeeb63bd8cf2013 Mon Sep 17 00:00:00 2001 From: sauwming Date: Tue, 17 Sep 2024 09:42:37 +0800 Subject: [PATCH 073/491] Fixed crash when making call if call setting is not supplied (#4074) --- pjsip/src/pjsua-lib/pjsua_call.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 0ee8fbdd2c..d099bfe3af 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -881,6 +881,8 @@ PJ_DEF(pj_status_t) pjsua_call_make_call(pjsua_acc_id acc_id, if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Failed to apply call setting", status); goto on_error; + } else if (!opt) { + opt = &call->opt; } /* Create sound port if none is instantiated, to check if sound device From 6cb98ab757e20c60b827dabda47f3d4de0fbb871 Mon Sep 17 00:00:00 2001 From: Riza Sulistyo Date: Wed, 25 Sep 2024 16:50:36 +0700 Subject: [PATCH 074/491] Add swig option to include doc for java and python wrapper (#4080) * Add swig option to include doc for java and python wrapper * Fix : unmappable character (0x80) for encoding US-ASCII --- pjsip-apps/src/swig/java/Makefile | 3 +++ pjsip-apps/src/swig/python/Makefile | 3 ++- pjsip/include/pjsua2/siptypes.hpp | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pjsip-apps/src/swig/java/Makefile b/pjsip-apps/src/swig/java/Makefile index c635266583..172a35571d 100644 --- a/pjsip-apps/src/swig/java/Makefile +++ b/pjsip-apps/src/swig/java/Makefile @@ -9,6 +9,9 @@ SWIG_FLAGS=-I../../../../pjlib/include \ SRC_DIR=../../../../pjsip/include SRCS=$(SRC_DIR)/pjsua2/endpoint.hpp $(SRC_DIR)/pjsua2/types.hpp +GEN_DOC = -doxygen +SWIG_FLAGS += $(GEN_DOC) + ifneq ($(findstring android,$(TARGET_NAME)),) OS=android ifeq ("$(JAVA_HOME)","") diff --git a/pjsip-apps/src/swig/python/Makefile b/pjsip-apps/src/swig/python/Makefile index ffe4f86ffe..7da0ed4772 100644 --- a/pjsip-apps/src/swig/python/Makefile +++ b/pjsip-apps/src/swig/python/Makefile @@ -26,10 +26,11 @@ SWIG_FLAGS=-I../../../../pjlib/include \ SRC_DIR=../../../../pjsip/include SRCS=$(SRC_DIR)/pjsua2/endpoint.hpp $(SRC_DIR)/pjsua2/types.hpp +GEN_DOC = -doxygen USE_THREADS = -threads # In SWIG 4.2 the build will fail if we use this flag #-DSWIG_NO_EXPORT_ITERATOR_METHODS -SWIG_FLAGS += -w312 $(USE_THREADS) +SWIG_FLAGS += -w312 $(USE_THREADS) $(GEN_DOC) .PHONY: all install uninstall diff --git a/pjsip/include/pjsua2/siptypes.hpp b/pjsip/include/pjsua2/siptypes.hpp index 9705bcc711..1c1914564b 100644 --- a/pjsip/include/pjsua2/siptypes.hpp +++ b/pjsip/include/pjsua2/siptypes.hpp @@ -492,7 +492,7 @@ struct TransportConfig : public PersistentObject /** * This specifies TLS settings for TLS transport. - * It’s only used when creating a SIP TLS transport. + * It's only used when creating a SIP TLS transport. */ TlsConfig tlsConfig; From 906f8168adee741488eec3bcfcf87e0f40703641 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 26 Sep 2024 09:29:54 +0700 Subject: [PATCH 075/491] Update PJSUA2 docs on auth digest AKA callback (#4089) --- pjsip/include/pjsua2/endpoint.hpp | 95 ++++++++++++++++--------------- pjsip/src/pjsua2/account.cpp | 5 +- pjsip/src/pjsua2/endpoint.cpp | 35 ++++++------ 3 files changed, 72 insertions(+), 63 deletions(-) diff --git a/pjsip/include/pjsua2/endpoint.hpp b/pjsip/include/pjsua2/endpoint.hpp index c446d04081..133159267d 100644 --- a/pjsip/include/pjsua2/endpoint.hpp +++ b/pjsip/include/pjsua2/endpoint.hpp @@ -546,57 +546,57 @@ struct DigestChallenge struct DigestCredential { /** - *Realm of the credential + * Realm of the credential */ std::string realm; /** - *Other parameters. + * Other parameters. */ StringToStringMap otherParam; /** - *Username parameter. + * Username parameter. */ std::string username; /** - *Nonce parameter. + * Nonce parameter. */ std::string nonce; /** - *URI parameter. + * URI parameter. */ std::string uri; /** - *Response digest. + * Response digest. */ std::string response; /** - *Algorithm. + * Algorithm. */ std::string algorithm; /** - *Cnonce. + * Cnonce. */ std::string cnonce; /** - *Opaque value. + * Opaque value. */ std::string opaque; /** - *Quality of protection. + * Quality of protection. */ std::string qop; /** - *Nonce count. + * Nonce count. */ std::string nc; @@ -613,20 +613,36 @@ struct DigestCredential /** - * Parameters for onCredAuth account method. + * Parameter of Endpoint::onCredAuth() callback. */ struct OnCredAuthParam { - /** Digest challenge */ + /** + * Digest challenge. + * The authentication challenge sent by server in 401 or 401 response, + * as either Proxy-Authenticate or WWW-Authenticate header. + */ DigestChallenge digestChallenge; - /** Credential info */ + /** + * Credential info. + */ AuthCredInfo credentialInfo; - /** Method */ + /** + * The request method. + */ std::string method; - /** Digest credential */ + /** + * The digest credential where the digest response will be placed to. + * + * Upon calling this function, the nonce, nc, cnonce, qop, uri, and realm + * fields of this structure must be set by the caller. + * + * Upon return, the callback must set the response in + * \a DigestCredential.response. + */ DigestCredential digestCredential; }; @@ -1928,35 +1944,24 @@ class Endpoint { PJ_UNUSED_ARG(prm); } /** - * Callback for computation of the digest credential. - * - * Usually, an application does not need to implement (overload) this callback. - * Use it, if your application needs to support Digest AKA authentication without - * the default digest computation back-end (i.e: using libmilenage). - * - * To use Digest AKA authentication, add \a PJSIP_CRED_DATA_EXT_AKA flag in the - * AuthCredInfo's \a dataType field of the AccountConfig, and fill up other - * AKA specific information in AuthCredInfo: - * - If PJSIP_HAS_DIGEST_AKA_AUTH is disabled, you have to overload this callback - * to provide your own digest computation back-end. - * - If PJSIP_HAS_DIGEST_AKA_AUTH is enabled, libmilenage library from - * \a third_party directory is linked, and this callback returns PJ_ENOTSUP, - * then the default digest computation back-end is used. - * - * @param prm.digestChallenge The authentication challenge sent by server in 401 - * or 401 response, as either Proxy-Authenticate or - * WWW-Authenticate header. - * @param prm.credentialInfo The credential to be used. - * @param method The request method. - * @param prm.digestCredential The digest credential where the digest response - * will be placed to. Upon calling this function, the - * nonce, nc, cnonce, qop, uri, and realm fields of - * this structure must have been set by caller. Upon - * return, the \a response field will be initialized - * by this function. - * - * @return PJ_ENOTSUP is the default. If you overload this callback, - * return PJ_SUCCESS on success. + * Callback for custom computation of the digest AKA response. + * + * Usually an application does not need to implement (overload) this + * callback because by default the response digest AKA is automatically + * computed using libmilenage. + * + * To use Digest AKA authentication, add \a PJSIP_CRED_DATA_EXT_AKA flag + * in the AuthCredInfo's \a dataType field of the AccountConfig, and + * fill up other AKA specific information in AuthCredInfo. + * Please see \ref PJSIP_AUTH_AKA_API for more information. + * + * @param prm Callback parameter. + * + * @return Return PJ_ENOTSUP to let the library compute + * the response digest automatically. + * Return PJ_SUCCESS if application does the computation + * and sets the response digest in + * \a prm.DigestCredential.response. */ virtual pj_status_t onCredAuth(OnCredAuthParam &prm); diff --git a/pjsip/src/pjsua2/account.cpp b/pjsip/src/pjsua2/account.cpp index 4db9214fe7..2558596fdd 100644 --- a/pjsip/src/pjsua2/account.cpp +++ b/pjsip/src/pjsua2/account.cpp @@ -992,8 +992,9 @@ void Account::create(const AccountConfig &acc_cfg, acc_cfg.toPj(pj_acc_cfg); for (unsigned i = 0; i < pj_acc_cfg.cred_count; ++i) { - pjsip_cred_info *dst = &pj_acc_cfg.cred_info[i]; - dst->ext.aka.cb = (pjsip_cred_cb)Endpoint::on_auth_create_aka_response_callback; + pjsip_cred_info *dst = &pj_acc_cfg.cred_info[i]; + dst->ext.aka.cb = (pjsip_cred_cb) + &Endpoint::on_auth_create_aka_response_callback; } pj_acc_cfg.user_data = (void*)this; PJSUA2_CHECK_EXPR( pjsua_acc_add(&pj_acc_cfg, make_default, &id) ); diff --git a/pjsip/src/pjsua2/endpoint.cpp b/pjsip/src/pjsua2/endpoint.cpp index b6fc1aed6d..829f70a300 100644 --- a/pjsip/src/pjsua2/endpoint.cpp +++ b/pjsip/src/pjsua2/endpoint.cpp @@ -2701,6 +2701,7 @@ pj_status_t Endpoint::on_auth_create_aka_response_callback(pj_pool_t *pool, pjsip_digest_credential *auth) { OnCredAuthParam prm; + prm.digestChallenge.fromPj(*chal); prm.credentialInfo.fromPj(*cred); prm.method = pj2Str(*method); @@ -2709,24 +2710,26 @@ pj_status_t Endpoint::on_auth_create_aka_response_callback(pj_pool_t *pool, pj_status_t status = Endpoint::instance().onCredAuth(prm); if (status == PJ_SUCCESS) { - pjsip_digest_credential auth_new = prm.digestCredential.toPj(); - // Duplicate in the pool, so that digestCredential - // is allowed to be destructed at the end of the method. - pj_strdup(pool, &auth->realm, &auth_new.realm); - pj_strdup(pool, &auth->username, &auth_new.username); - pj_strdup(pool, &auth->nonce, &auth_new.nonce); - pj_strdup(pool, &auth->uri, &auth_new.uri); - pj_strdup(pool, &auth->response, &auth_new.response); - pj_strdup(pool, &auth->algorithm, &auth_new.algorithm); - pj_strdup(pool, &auth->cnonce, &auth_new.cnonce); - pj_strdup(pool, &auth->opaque, &auth_new.opaque); - pj_strdup(pool, &auth->qop, &auth_new.qop); - pj_strdup(pool, &auth->nc, &auth_new.nc); - pjsip_param_clone(pool, &auth->other_param, &auth_new.other_param); + pjsip_digest_credential auth_new = prm.digestCredential.toPj(); + + // Duplicate in the pool, so that digestCredential + // is allowed to be destructed at the end of the method. + pj_strdup(pool, &auth->realm, &auth_new.realm); + pj_strdup(pool, &auth->username, &auth_new.username); + pj_strdup(pool, &auth->nonce, &auth_new.nonce); + pj_strdup(pool, &auth->uri, &auth_new.uri); + pj_strdup(pool, &auth->response, &auth_new.response); + pj_strdup(pool, &auth->algorithm, &auth_new.algorithm); + pj_strdup(pool, &auth->cnonce, &auth_new.cnonce); + pj_strdup(pool, &auth->opaque, &auth_new.opaque); + pj_strdup(pool, &auth->qop, &auth_new.qop); + pj_strdup(pool, &auth->nc, &auth_new.nc); + pjsip_param_clone(pool, &auth->other_param, &auth_new.other_param); } #if PJSIP_HAS_DIGEST_AKA_AUTH - else if (status == PJ_ENOTSUP) { - status = pjsip_auth_create_aka_response(pool, chal, cred, method, auth); + else if (status == PJ_ENOTSUP) { + status = pjsip_auth_create_aka_response(pool, chal, cred, method, + auth); } #endif return status; From 7249136f8f796eea8342b1f6c7fe73b45a8d20a6 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 1 Oct 2024 10:56:16 +0700 Subject: [PATCH 076/491] Update IP version choosing logic in media transport creation for generating SDP offer. (#4067) --- pjlib/include/pj/addr_resolv.h | 45 ++++++++++ pjlib/src/pj/sock_common.c | 111 ++++++++++++++++++++++++ pjsip/src/pjsua-lib/pjsua_media.c | 135 +++++++++++++++++++++++++++--- 3 files changed, 279 insertions(+), 12 deletions(-) diff --git a/pjlib/include/pj/addr_resolv.h b/pjlib/include/pj/addr_resolv.h index 90f2a57f07..ecd5e8994f 100644 --- a/pjlib/include/pj/addr_resolv.h +++ b/pjlib/include/pj/addr_resolv.h @@ -180,6 +180,51 @@ PJ_DECL(pj_status_t) pj_getaddrinfo(int af, const pj_str_t *name, unsigned *count, pj_addrinfo ai[]); +/** + * Enumeration of IP address type. + */ +typedef enum pj_addr_type +{ + /** + * Disabled: 0.0.0.0/8 or ::/128 + */ + PJ_ADDR_TYPE_DISABLED = 1, + + /** + * Loopback: 127.0.0.0/8 or ::1/128 + */ + PJ_ADDR_TYPE_LOOPBACK = 2, + + /** + * Link-local: 169.254.0.0/16 or fe80::/64 + */ + PJ_ADDR_TYPE_LINK_LOCAL = 4, + + /** + * Private: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 or fc00::/7 + */ + PJ_ADDR_TYPE_PRIVATE = 8, + + /** + * Multicast: 224.0.0.0/3 or ff00::/8 + */ + PJ_ADDR_TYPE_MULTICAST = 16, + +} pj_addr_type; + + + +/** + * Check IP address type. + * + * @param addr The IP address to check, must be IPv4 or IPv6 address. + * @param type Bit mask of address type pj_addr_type. + * + * @return PJ_TRUE if the type of specified address \addr match to + * the specified types \a type. + */ +PJ_DECL(pj_bool_t) pj_check_addr_type(const pj_sockaddr *addr, unsigned type); + /** @} */ diff --git a/pjlib/src/pj/sock_common.c b/pjlib/src/pj/sock_common.c index 5c8d8a63dd..62b08bdea1 100644 --- a/pjlib/src/pj/sock_common.c +++ b/pjlib/src/pj/sock_common.c @@ -731,6 +731,7 @@ PJ_DEF(pj_status_t) pj_sockaddr_parse( int af, unsigned options, return status; } + /* Resolve the IP address of local machine */ PJ_DEF(pj_status_t) pj_gethostip(int af, pj_sockaddr *addr) { @@ -1410,6 +1411,116 @@ PJ_DEF(pj_status_t) pj_sock_socketpair(int family, #endif +/* Check IP address type. */ +PJ_DEF(pj_bool_t) pj_check_addr_type(const pj_sockaddr *addr, unsigned type) +{ + int af; + + PJ_ASSERT_RETURN(addr && type, PJ_EINVAL); + PJ_ASSERT_RETURN(addr->addr.sa_family == PJ_AF_INET || + addr->addr.sa_family == PJ_AF_INET6, PJ_EINVAL); + + af = addr->addr.sa_family; + + if (af == PJ_AF_INET) { + enum { + /* Disabled: 0.0.0.0/8 */ + ADDR4_DISABLED = 0x00000000, + MASK4_DISABLED = 0xFF000000, + /* Loopback: 127.0.0.0/8 */ + ADDR4_LOOPBACK = 0x7f000000, + MASK4_LOOPBACK = 0xFF000000, + /* Link-local: 169.254.0.0/16 */ + ADDR4_LINKLOCAL = 0xa9fe0000, + MASK4_LINKLOCAL = 0xFFFF0000, + /* Multicast: 224.0.0.0/3 */ + ADDR4_MULTICAST = 0xE0000000, + MASK4_MULTICAST = 0xF0000000, + }; + + pj_uint32_t a = pj_ntohl(addr->ipv4.sin_addr.s_addr); + + if ((type & PJ_ADDR_TYPE_DISABLED) && + (a & (pj_uint32_t)MASK4_DISABLED)==(pj_uint32_t)ADDR4_DISABLED) + { + return PJ_TRUE; + } + + if ((type & PJ_ADDR_TYPE_LOOPBACK) && + (a & (pj_uint32_t)MASK4_LOOPBACK)==(pj_uint32_t)ADDR4_LOOPBACK) + { + return PJ_TRUE; + } + + if ((type & PJ_ADDR_TYPE_LINK_LOCAL) && + (a & (pj_uint32_t)MASK4_LINKLOCAL)==(pj_uint32_t)ADDR4_LINKLOCAL) + { + return PJ_TRUE; + } + + if (type & PJ_ADDR_TYPE_PRIVATE) { + pj_uint8_t b1 = (pj_uint8_t)(a >> 24); + pj_uint8_t b2 = (pj_uint8_t)((a >> 16) & 0x0ff);; + + /* 10.0.0.0/8 */ + if (b1 == 10) + return PJ_TRUE; + + /* 172.16.0.0/12 or 172.16.0.0-172.31.255.255 */ + if ((b1 == 172) && (b2 >= 16) && (b2 <= 31)) + return PJ_TRUE; + + /* 192.168.0.0/16 */ + if ((b1 == 192) && (b2 == 168)) + return PJ_TRUE; + } + + if ((type & PJ_ADDR_TYPE_MULTICAST) && + (a & (pj_uint32_t)MASK4_MULTICAST)==(pj_uint32_t)ADDR4_MULTICAST) + { + return PJ_TRUE; + } + + } else /* if (af == PJ_AF_INET6) */ { + + if (type & PJ_ADDR_TYPE_DISABLED) { + /* ::/128 */ + pj_uint8_t saddr[16] = {0x0,0x0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + if (pj_memcmp(saddr, &addr->ipv6.sin6_addr, 16) == 0) + return PJ_TRUE; + } + + if (type & PJ_ADDR_TYPE_LOOPBACK) { + /* ::1/128 */ + pj_uint8_t saddr[16] = {0x0,0x0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; + if (pj_memcmp(saddr, &addr->ipv6.sin6_addr, 16) == 0) + return PJ_TRUE; + } + + if (type & PJ_ADDR_TYPE_LINK_LOCAL) { + /* fe80::/64 */ + pj_uint8_t saddr[16] = {0xfe,0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + if (pj_memcmp(saddr, &addr->ipv6.sin6_addr, 8) == 0) + return PJ_TRUE; + } + + if (type & PJ_ADDR_TYPE_PRIVATE) { + /* fc00::/7 */ + if ((addr->ipv6.sin6_addr.s6_addr[0] & 0xfe) == 0xfc) + return PJ_TRUE; + } + + if (type & PJ_ADDR_TYPE_MULTICAST) { + /* ff00::/8 */ + if (addr->ipv6.sin6_addr.s6_addr[0] == 0xff) + return PJ_TRUE; + } + } + + return PJ_FALSE; +} + + /* Only need to implement these in DLL build */ #if defined(PJ_DLL) diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index 01d4e4ce8f..4c92fb6b57 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -247,7 +247,9 @@ pj_status_t pjsua_media_subsys_destroy(unsigned flags) } static int get_media_ip_version(pjsua_call_media *call_med, - const pjmedia_sdp_session *rem_sdp) + const pjmedia_sdp_session *rem_sdp, + pj_bool_t loop_tp, + pj_bool_t ice_tp) { #if PJ_HAS_IPV6 @@ -270,13 +272,118 @@ static int get_media_ip_version(pjsua_call_media *call_med, /* Use IPv6. */ return 6; } + } else { - if (ipv6_use == PJSUA_IPV6_ENABLED_PREFER_IPV6 || - ipv6_use == PJSUA_IPV6_ENABLED_USE_IPV6_ONLY) - { - /* Use IPv6. */ + pj_sockaddr addr = {{0}}; + pj_status_t status; + + /* Status: + * - not avail: no IP address, gethostip() returns error. + * - available: IP address is loopback, local-link, or disabled. + * - usable : IP address is not loopback, local-link, nor disabled. + */ + unsigned ipv6_status = 0; /* 0: not avail, 1: available, 2: usable*/ + unsigned ipv4_status = 0; /* 0: not avail, 1: available, 2: usable*/ + + /* Use IPv4 regardless. */ + if (ipv6_use == PJSUA_IPV6_DISABLED) + return 4; + + /* Use IPv6 regardless. */ + if (ipv6_use == PJSUA_IPV6_ENABLED_USE_IPV6_ONLY) return 6; + + /* For loop transport, use the preferred */ + if (loop_tp) { + if (ipv6_use == PJSUA_IPV6_ENABLED_PREFER_IPV6) + return 6; + if (ipv6_use == PJSUA_IPV6_ENABLED_PREFER_IPV4) + return 4; + + /* No preference, just use IPv4 */ + return 4; } + + + /* Check IPv6 & IPv4 availability & usability */ + + status = pj_gethostip(PJ_AF_INET6, &addr); + if (status == PJ_SUCCESS) { + /* IPv6 is available */ + ipv6_status = 1; + + /* Check if it is usable */ + if (!pj_check_addr_type(&addr, + PJ_ADDR_TYPE_DISABLED | + PJ_ADDR_TYPE_LOOPBACK | + PJ_ADDR_TYPE_LINK_LOCAL)) + { + /* If IPv6 is usable & preferred, use it */ + if (ipv6_use == PJSUA_IPV6_ENABLED_PREFER_IPV6) + return 6; + + /* IPv6 seems usable */ + ipv6_status = 2; + } + } + + /* For ICE transport, use IPv6 if available */ + if (ice_tp) { + return (ipv6_status >= 1)? 6 : 4; + } + + status = pj_gethostip(PJ_AF_INET, &addr); + if (status == PJ_SUCCESS) { + /* IPv4 is available */ + ipv4_status = 1; + + /* Check if it is usable */ + if (!pj_check_addr_type(&addr, + PJ_ADDR_TYPE_DISABLED | + PJ_ADDR_TYPE_LOOPBACK | + PJ_ADDR_TYPE_LINK_LOCAL)) + { + /* If IPv4 is usable & preferred, use it */ + if (ipv6_use == PJSUA_IPV6_ENABLED_PREFER_IPV4) + return 4; + + /* IPv4 seems usable */ + ipv4_status = 2; + } + } + + + /* Preferred IP version is not usable, fallback to any usable. + * In this point, either only one is usable (but not preferred) + * or there is no preference (IPv4 will be returned). + */ + if (ipv4_status == 2) + return 4; + if (ipv6_status == 2) + return 6; + + PJ_LOG(3,(THIS_FILE,"Call %d: Media %d: Warning, no usable " + "IP address for SDP offer", + call_med->call->index, call_med->idx)); + + /* No usable IP version, pick based on availability & preference. */ + if (ipv6_use == PJSUA_IPV6_ENABLED_PREFER_IPV6 && ipv6_status > 0) + return 6; + if (ipv6_use == PJSUA_IPV6_ENABLED_PREFER_IPV4 && ipv4_status > 0) + return 4; + + /* Preferred IP version is not available, fallback to any available. + * In this point, either only one is available (but not preferred) + * or there is no preference (IPv4 will be returned). + */ + if (ipv4_status == 1) + return 4; + if (ipv6_status == 1) + return 6; + + PJ_LOG(3,(THIS_FILE,"Call %d: Media %d: Warning, no available " + "IP address for SDP offer", + call_med->call->index, call_med->idx)); } #else PJ_UNUSED_ARG(call_med); @@ -309,7 +416,7 @@ static pj_status_t create_rtp_rtcp_sock(pjsua_call_media *call_med, pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id]; pj_sock_t sock[2]; - use_ipv6 = (get_media_ip_version(call_med, rem_sdp) == 6); + use_ipv6 = (get_media_ip_version(call_med, rem_sdp, PJ_FALSE, PJ_FALSE)==6); use_nat64 = PJ_HAS_IPV6 && (acc->cfg.nat64_opt != PJSUA_NAT64_DISABLED); af = (use_ipv6 || use_nat64) ? pj_AF_INET6() : pj_AF_INET(); @@ -762,7 +869,7 @@ static pj_status_t create_loop_media_transport( int af; pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id]; - use_ipv6 = (get_media_ip_version(call_med, rem_sdp) == 6); + use_ipv6 = (get_media_ip_version(call_med, rem_sdp, PJ_TRUE, PJ_FALSE)==6); use_nat64 = PJ_HAS_IPV6 && (acc->cfg.nat64_opt != PJSUA_NAT64_DISABLED); af = (use_ipv6 || use_nat64) ? pj_AF_INET6() : pj_AF_INET(); @@ -1020,7 +1127,8 @@ static pj_status_t create_ice_media_transport( pjmedia_sdp_session *rem_sdp; acc_cfg = &pjsua_var.acc[call_med->call->acc_id].cfg; - use_ipv6 = (get_media_ip_version(call_med, remote_sdp) == 6); + use_ipv6 = (get_media_ip_version(call_med, remote_sdp, PJ_FALSE, PJ_TRUE) + == 6); use_nat64 = PJ_HAS_IPV6 && (acc_cfg->nat64_opt != PJSUA_NAT64_DISABLED); /* Make sure STUN server resolution has completed */ @@ -2824,11 +2932,14 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, pj_bool_t use_nat64; if (rem_sdp) { - use_ipv6 = (get_media_ip_version(call_med, rem_sdp) == 6); + use_ipv6 = (get_media_ip_version(call_med, rem_sdp, + PJ_FALSE, PJ_FALSE) == 6); } else { - use_ipv6 = PJ_HAS_IPV6 && - (pjsua_var.acc[call->acc_id].cfg.ipv6_media_use != - PJSUA_IPV6_DISABLED); + //use_ipv6 = PJ_HAS_IPV6 && + // (pjsua_var.acc[call->acc_id].cfg.ipv6_media_use != + // PJSUA_IPV6_DISABLED); + use_ipv6 = (get_media_ip_version(call_med, rem_sdp, + PJ_FALSE, PJ_FALSE) == 6); } use_nat64 = PJ_HAS_IPV6 && (pjsua_var.acc[call->acc_id].cfg.nat64_opt != From 7b93726e76d38abaf3b56b8e75a9bce125c10ad0 Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 2 Oct 2024 12:45:59 +0800 Subject: [PATCH 077/491] Fix UPnP IGD search and port mapping deletion (#4094) --- pjnath/src/pjnath/upnp.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pjnath/src/pjnath/upnp.c b/pjnath/src/pjnath/upnp.c index f16a679ee0..8e0e5574ae 100644 --- a/pjnath/src/pjnath/upnp.c +++ b/pjnath/src/pjnath/upnp.c @@ -48,7 +48,7 @@ /* UPnP device descriptions. */ static const char* UPNP_ROOT_DEVICE = "upnp:rootdevice"; static const char* UPNP_IGD_DEVICE = - "urn:schemas-upnp-org:device:InternetGatewayDevice:1"; + "urn:schemas-upnp-org:device:InternetGatewayDevice"; static const char* UPNP_WANIP_SERVICE = "urn:schemas-upnp-org:service:WANIPConnection:1"; static const char* UPNP_WANPPP_SERVICE = @@ -215,7 +215,9 @@ static void download_igd_xml(unsigned dev_idx) /* Check device type. */ dev_type = doc_get_elmt_value(doc, "deviceType"); if (!dev_type) return; - if (pj_ansi_strcmp(dev_type, UPNP_IGD_DEVICE) != 0) { + if (pj_ansi_strncmp(dev_type, UPNP_IGD_DEVICE, + pj_ansi_strlen(UPNP_IGD_DEVICE)) != 0) + { /* Device type is not IGD. */ goto on_error; } @@ -515,7 +517,8 @@ static int client_cb(Upnp_EventType event_type, const void *event, if (!check_error_response(response)) { PJ_LOG(4, (THIS_FILE, "Successfully deleted port mapping")); } - ixmlDocument_free(response); + /* According to the sample, we don't need to free this. */ + // ixmlDocument_free(response); } break; From 146f1f222782927146fedc20cb41f0ba106d0d02 Mon Sep 17 00:00:00 2001 From: sauwming Date: Thu, 3 Oct 2024 16:02:19 +0800 Subject: [PATCH 078/491] Fixed incorrect buffer size in Android video codec (#4095) --- pjmedia/src/pjmedia-codec/and_vid_mediacodec.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pjmedia/src/pjmedia-codec/and_vid_mediacodec.cpp b/pjmedia/src/pjmedia-codec/and_vid_mediacodec.cpp index 6391e479cf..cb898f3108 100644 --- a/pjmedia/src/pjmedia-codec/and_vid_mediacodec.cpp +++ b/pjmedia/src/pjmedia-codec/and_vid_mediacodec.cpp @@ -61,7 +61,7 @@ #define SPS_PPS_BUF_SIZE 64 -#define MAX_RX_WIDTH 1200 +#define MAX_RX_WIDTH 1280 #define MAX_RX_HEIGHT 800 /* Maximum duration from one key frame to the next (in seconds). */ @@ -1350,7 +1350,7 @@ static pj_status_t and_media_decode(pjmedia_vid_codec *codec, return status; } len = write_yuv((pj_uint8_t *)output->buf, - and_media_data->dec_buf_info.size, + output->size, output_buf, and_media_data->dec_stride_len, and_media_data->prm->dec_fmt.det.vid.size.w, From e289ddf3d80c61ff5ec37ce4ca49a24997103a16 Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 4 Oct 2024 12:57:47 +0800 Subject: [PATCH 079/491] Fixed doc for sending DTMF (#4099) --- pjsip/include/pjsua-lib/pjsua.h | 3 ++- pjsip/include/pjsua2/call.hpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index d0951b3cf5..0bec35bd8d 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -5623,7 +5623,8 @@ typedef struct pjsua_call_send_dtmf_param pjsua_dtmf_method method; /** - * The signal duration used for the DTMF. + * The signal duration used for the DTMF. This field is only used + * if the method is PJSUA_DTMF_METHOD_SIP_INFO. * * Default: PJSUA_CALL_SEND_DTMF_DURATION_DEFAULT */ diff --git a/pjsip/include/pjsua2/call.hpp b/pjsip/include/pjsua2/call.hpp index 743da677ac..d37df81b02 100644 --- a/pjsip/include/pjsua2/call.hpp +++ b/pjsip/include/pjsua2/call.hpp @@ -1355,7 +1355,8 @@ struct CallSendDtmfParam pjsua_dtmf_method method; /** - * The signal duration used for the DTMF. + * The signal duration used for the DTMF. This field is only used + * if the method is PJSUA_DTMF_METHOD_SIP_INFO. * * Default: PJSUA_CALL_SEND_DTMF_DURATION_DEFAULT */ From f6fce7a66ccb03a0e00f6717668aeabb9988fb3b Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 4 Oct 2024 18:07:49 +0800 Subject: [PATCH 080/491] Android audio JNI: prevent double start (#4100) --- pjmedia/src/pjmedia-audiodev/android_jni_dev.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pjmedia/src/pjmedia-audiodev/android_jni_dev.c b/pjmedia/src/pjmedia-audiodev/android_jni_dev.c index 85ea3b3a31..65a2cc0372 100644 --- a/pjmedia/src/pjmedia-audiodev/android_jni_dev.c +++ b/pjmedia/src/pjmedia-audiodev/android_jni_dev.c @@ -185,7 +185,9 @@ static int AndroidRecorderCallback(void *userData) /* Start recording */ pj_thread_set_prio(NULL, THREAD_PRIORITY_URGENT_AUDIO); - (*jni_env)->CallVoidMethod(jni_env, stream->record, record_method); + pj_sem_wait(stream->rec_sem); + if (!stream->quit_flag) + (*jni_env)->CallVoidMethod(jni_env, stream->record, record_method); while (!stream->quit_flag) { pjmedia_frame frame; @@ -278,7 +280,9 @@ static int AndroidTrackCallback(void *userData) /* Start playing */ pj_thread_set_prio(NULL, THREAD_PRIORITY_URGENT_AUDIO); - (*jni_env)->CallVoidMethod(jni_env, stream->track, play_method); + pj_sem_wait(stream->play_sem); + if (!stream->quit_flag) + (*jni_env)->CallVoidMethod(jni_env, stream->track, play_method); while (!stream->quit_flag) { pjmedia_frame frame; From e8eab5ea0f709c5b654626f2fad25cb7a8001853 Mon Sep 17 00:00:00 2001 From: sauwming Date: Tue, 8 Oct 2024 12:12:23 +0800 Subject: [PATCH 081/491] Added more logs (#4098) --- pjlib/include/pj/assert.h | 44 ++++++++++++++++++++++++++++--- pjlib/src/pj/os_core_win32.c | 31 +++++++++++++--------- pjmedia/src/pjmedia/silencedet.c | 24 +++++++++++------ pjmedia/src/pjmedia/stream.c | 2 +- pjsip/src/pjsip/sip_dialog.c | 4 +-- pjsip/src/pjsip/sip_transaction.c | 2 ++ pjsip/src/pjsua-lib/pjsua_aud.c | 5 +++- pjsip/src/pjsua-lib/pjsua_core.c | 2 ++ 8 files changed, 86 insertions(+), 28 deletions(-) diff --git a/pjlib/include/pj/assert.h b/pjlib/include/pj/assert.h index 4f13d92d9c..bf830c4684 100644 --- a/pjlib/include/pj/assert.h +++ b/pjlib/include/pj/assert.h @@ -40,14 +40,52 @@ * Check during debug build that an expression is true. If the expression * computes to false during run-time, then the program will stop at the * offending statements. - * For release build, this macro will not do anything. + * For release build, this macro will only log the assertion, while the + * program continues running. * * @param expr The expression to be evaluated. */ -#ifndef pj_assert -# define pj_assert(expr) assert(expr) +#if PJ_DEBUG==0 + +# ifndef pj_assert +# include "pj/os.h" +# include "pj/log.h" +# define pj_assert(expr) \ + do { \ + if (!(expr)) { \ + if (pj_thread_is_registered()) \ + PJ_LOG(1, (__FILE__, "Assert failed: %s", #expr)); \ + } \ + } while (0) +# endif + +#else /* PJ_DEBUG != 0 */ + +# ifndef pj_assert +# define pj_assert(expr) assert(expr) +# endif + #endif +/** + * @hideinitializer + * For all builds, log the message. + * Check during debug build that an expression is true. If the expression + * computes to false during run-time, then the program will stop at the + * offending statements. + * For release build, this macro only print message on the log. + * @param expr The expression to be evaluated. + * @param ... File name. The format string for the log message + * ("config.c", " PJ_VERSION: %s", PJ_VERSION) + */ +#ifndef PJ_ASSERT_LOG +#include "pj/log.h" +#define PJ_ASSERT_LOG(expr,...) \ + do { \ + if (!(expr)) { PJ_LOG(1,(__VA_ARGS__)); assert(expr); } \ + } while (0) + +#endif /** * @hideinitializer diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c index 55921adcb0..4eb8c00fd1 100644 --- a/pjlib/src/pj/os_core_win32.c +++ b/pjlib/src/pj/os_core_win32.c @@ -54,7 +54,7 @@ #else # define LOG_MUTEX(expr) PJ_LOG(6,expr) #endif - +# define LOG_MUTEX_WARN(expr) PJ_LOG(3,expr) #define THIS_FILE "os_core_win32.c" /* @@ -1094,10 +1094,15 @@ PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) status = PJ_STATUS_FROM_OS(GetLastError()); #endif - LOG_MUTEX((mutex->obj_name, - (status==PJ_SUCCESS ? "Mutex acquired by thread %s" : "FAILED by %s"), - pj_thread_this()->obj_name)); - + if (status == PJ_SUCCESS) { + LOG_MUTEX((mutex->obj_name, + "Mutex acquired by thread %s", + pj_thread_this()->obj_name)); + } else { + LOG_MUTEX_WARN((mutex->obj_name, status, + "Failed to acquire mutex by thread %s", + pj_thread_this()->obj_name)); + } #if PJ_DEBUG if (status == PJ_SUCCESS) { mutex->owner = pj_thread_this(); @@ -1279,6 +1284,7 @@ PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, static pj_status_t pj_sem_wait_for(pj_sem_t *sem, unsigned timeout) { DWORD result; + pj_status_t status = PJ_SUCCESS; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sem, PJ_EINVAL); @@ -1296,16 +1302,15 @@ static pj_status_t pj_sem_wait_for(pj_sem_t *sem, unsigned timeout) LOG_MUTEX((sem->obj_name, "Semaphore acquired by thread %s", pj_thread_this()->obj_name)); } else { - LOG_MUTEX((sem->obj_name, "Semaphore: thread %s FAILED to acquire", - pj_thread_this()->obj_name)); + if (result == WAIT_TIMEOUT) + status = PJ_ETIMEDOUT; + else + status = PJ_RETURN_OS_ERROR(GetLastError()); + LOG_MUTEX_WARN((sem->obj_name, status, "Semaphore: thread %s failed " + "to acquire", pj_thread_this()->obj_name)); } - if (result==WAIT_OBJECT_0) - return PJ_SUCCESS; - else if (result==WAIT_TIMEOUT) - return PJ_ETIMEDOUT; - else - return PJ_RETURN_OS_ERROR(GetLastError()); + return status; } /* diff --git a/pjmedia/src/pjmedia/silencedet.c b/pjmedia/src/pjmedia/silencedet.c index a2f8964c46..1fb8284ea2 100644 --- a/pjmedia/src/pjmedia/silencedet.c +++ b/pjmedia/src/pjmedia/silencedet.c @@ -235,9 +235,11 @@ PJ_DEF(pj_bool_t) pjmedia_silence_det_apply( pjmedia_silence_det *sd, /* Voiced for long time (>recalc_on_voiced), current * threshold seems to be too low. */ + unsigned old = sd->threshold; sd->threshold = (avg_recent_level + sd->threshold) >> 1; - TRACE_((THIS_FILE,"Re-adjust threshold (in talk burst)" - "to %d", sd->threshold)); + if (sd->threshold != old) + TRACE_((THIS_FILE,"%s re-adjust threshold (in talk burst) to %d (was %d)", + sd->objname, sd->threshold, old)); sd->voiced_timer = 0; @@ -248,8 +250,9 @@ PJ_DEF(pj_bool_t) pjmedia_silence_det_apply( pjmedia_silence_det *sd, break; case STATE_SILENCE: - TRACE_((THIS_FILE,"Starting talk burst (level=%d threshold=%d)", - level, sd->threshold)); + TRACE_((THIS_FILE,"%s starting talk burst (level=%d " + "threshold=%d)", sd->objname, level, + sd->threshold)); case STATE_START_SILENCE: sd->state = STATE_VOICED; @@ -271,9 +274,13 @@ PJ_DEF(pj_bool_t) pjmedia_silence_det_apply( pjmedia_silence_det *sd, switch(sd->state) { case STATE_SILENCE: if (sd->silence_timer >= sd->recalc_on_silence) { + unsigned old = sd->threshold; sd->threshold = avg_recent_level << 1; - TRACE_((THIS_FILE,"Re-adjust threshold (in silence)" - "to %d", sd->threshold)); + if (sd->threshold != old) { + TRACE_((THIS_FILE,"%s re-adjust threshold (in silence)" + " to %d (was %d)", sd->objname, + sd->threshold, old)); + } sd->silence_timer = 0; @@ -294,8 +301,9 @@ PJ_DEF(pj_bool_t) pjmedia_silence_det_apply( pjmedia_silence_det *sd, if (sd->silence_timer >= sd->before_silence) { sd->state = STATE_SILENCE; sd->threshold = avg_recent_level << 1; - TRACE_((THIS_FILE,"Starting silence (level=%d " - "threshold=%d)", level, sd->threshold)); + TRACE_((THIS_FILE,"%s starting silence (level=%d " + "threshold=%d)", sd->objname, level, + sd->threshold)); /* Reset sig_level */ sd->sum_level = avg_recent_level; diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index d5d62aa431..4a9cb09e05 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -1571,7 +1571,7 @@ static pj_status_t put_frame_imp( pjmedia_port *port, pjmedia_rtp_hdr *rtp = (pjmedia_rtp_hdr*) channel->out_pkt; rtp->m = 1; - PJ_LOG(5,(stream->port.info.name.ptr,"Start talksprut..")); + PJ_LOG(5,(stream->port.info.name.ptr,"Starting talksprut..")); } stream->is_streaming = PJ_TRUE; diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c index 7df463d529..d83cd0d3c7 100644 --- a/pjsip/src/pjsip/sip_dialog.c +++ b/pjsip/src/pjsip/sip_dialog.c @@ -999,8 +999,8 @@ PJ_DEF(void) pjsip_dlg_dec_lock(pjsip_dialog *dlg) { PJ_ASSERT_ON_FAIL(dlg!=NULL, return); - PJ_LOG(6,(dlg->obj_name, "Entering pjsip_dlg_dec_lock(), sess_count=%d", - dlg->sess_count)); + PJ_LOG(6,(dlg->obj_name, "Entering pjsip_dlg_dec_lock(), sess_count=%d, " + "tsx_count=%d", dlg->sess_count, dlg->tsx_count)); pj_assert(dlg->sess_count > 0); --dlg->sess_count; diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c index 5d6031221d..8a6c18e1b8 100644 --- a/pjsip/src/pjsip/sip_transaction.c +++ b/pjsip/src/pjsip/sip_transaction.c @@ -2108,6 +2108,8 @@ static void send_msg_callback( pjsip_send_state *send_state, tsx_update_transport(tsx, send_state->cur_transport); /* Update remote address. */ + pj_assert(tdata->dest_info.cur_addr < + PJ_ARRAY_SIZE(tdata->dest_info.addr.entry)); tsx->addr_len = tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr_len; pj_memcpy(&tsx->addr, &tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr, diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index 591be2b83b..bca4c6a90b 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -701,7 +701,8 @@ pj_status_t pjsua_aud_channel_update(pjsua_call_media *call_med, PJ_UNUSED_ARG(local_sdp); PJ_UNUSED_ARG(remote_sdp); - PJ_LOG(4,(THIS_FILE,"Audio channel update..")); + PJ_LOG(4,(THIS_FILE,"Audio channel update for index %d for call %d...", + call_med->idx, call_med->call->index)); pj_log_push_indent(); si->rtcp_sdes_bye_disabled = pjsua_var.media_cfg.no_rtcp_sdes_bye; @@ -853,6 +854,8 @@ pj_status_t pjsua_aud_channel_update(pjsua_call_media *call_med, on_return: pj_log_pop_indent(); + if (status != PJ_SUCCESS) + pjsua_perror(THIS_FILE, "pjsua_aud_channel_update failed", status); return status; } diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 4cd94bd051..41aacda03f 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -743,6 +743,8 @@ PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg) { pj_status_t status; + PJ_ASSERT_RETURN(cfg, PJ_EINVAL); + /* Save config. */ pjsua_logging_config_dup(pjsua_var.pool, &pjsua_var.log_cfg, cfg); From 9c134f4d49d9d48c7269bf1c5f8df83cd90b2961 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 22 Oct 2024 10:51:39 +0700 Subject: [PATCH 082/491] Assertion in sending BYE after transaction timeout (#4107) * Fix assertion in sending BYE for a timeout transaction. * Add Reason header to BYE. --- pjsip/src/pjsip-ua/sip_inv.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c index 73197abc29..e93c3d6551 100644 --- a/pjsip/src/pjsip-ua/sip_inv.c +++ b/pjsip/src/pjsip-ua/sip_inv.c @@ -4664,11 +4664,19 @@ static pj_bool_t handle_uac_tsx_response(pjsip_inv_session *inv, pj_status_t status; inv_set_cause(inv, tsx->status_code, &tsx->status_text); - inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); + + /* Do not shift state to DISCONNECTED here, as it will destroy the + * invite session and the BYE sending below will raise an assertion. + */ + //inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); /* Send BYE */ status = pjsip_dlg_create_request(inv->dlg, pjsip_get_bye_method(), -1, &bye); + if (status == PJ_SUCCESS && tsx->status_text.slen) { + status = add_reason_warning_hdr(bye, tsx->status_code, + &tsx->status_text); + } if (status == PJ_SUCCESS) { pjsip_inv_send_msg(inv, bye); } @@ -5381,6 +5389,10 @@ static void inv_on_state_connecting( pjsip_inv_session *inv, pjsip_event *e) status = pjsip_dlg_create_request(inv->dlg, pjsip_get_bye_method(), -1, &bye); + if (status == PJ_SUCCESS && tsx->status_text.slen) { + status = add_reason_warning_hdr(bye, tsx->status_code, + &tsx->status_text); + } if (status == PJ_SUCCESS) { pjsip_inv_send_msg(inv, bye); From 486235219d91ef05c6afa09d2d20f07257a09338 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 29 Oct 2024 14:31:46 +0700 Subject: [PATCH 083/491] Asynchronous conference bridge operation (#3928) * Updates PJMEDIA ports to have their own pool, update docs, apply new port check in video conference (to avoid vconf put/get frame from just-added-yet-unsynced ports). * Fix avi streams group lock handler. * Fix premature destroy of cstream objects (codec, jb, etc). * Add log for debugging * Schedule pjsua app pool release using pjsip_endpt_atexit(). * Fix init_grp_lock() to add_ref() before add_handler(). --- pjmedia/include/pjmedia/conference.h | 16 + pjmedia/include/pjmedia/port.h | 14 +- pjmedia/include/pjmedia/splitcomb.h | 3 +- pjmedia/src/pjmedia/avi_player.c | 63 ++- pjmedia/src/pjmedia/conference.c | 700 +++++++++++++++++++++------ pjmedia/src/pjmedia/echo_port.c | 26 +- pjmedia/src/pjmedia/mem_capture.c | 18 +- pjmedia/src/pjmedia/mem_player.c | 16 +- pjmedia/src/pjmedia/null_port.c | 38 +- pjmedia/src/pjmedia/port.c | 35 +- pjmedia/src/pjmedia/splitcomb.c | 60 ++- pjmedia/src/pjmedia/stream.c | 74 ++- pjmedia/src/pjmedia/tonegen.c | 32 +- pjmedia/src/pjmedia/vid_conf.c | 41 +- pjmedia/src/pjmedia/wav_player.c | 71 ++- pjmedia/src/pjmedia/wav_playlist.c | 49 +- pjmedia/src/pjmedia/wav_writer.c | 67 ++- pjsip-apps/src/pjsua/pjsua_app.c | 23 +- pjsip/src/pjsua-lib/pjsua_aud.c | 9 +- 19 files changed, 1039 insertions(+), 316 deletions(-) diff --git a/pjmedia/include/pjmedia/conference.h b/pjmedia/include/pjmedia/conference.h index 9c1b4b4535..9000daaa9f 100644 --- a/pjmedia/include/pjmedia/conference.h +++ b/pjmedia/include/pjmedia/conference.h @@ -348,6 +348,10 @@ PJ_DECL(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf, * Disconnect unidirectional audio from the specified source to the specified * sink slot. * + * Note that the operation will be done asynchronously, so application + * should not assume that the port will no longer receive/send audio frame + * after this function has returned. + * * @param conf The conference bridge. * @param src_slot Source slot. * @param sink_slot Sink slot. @@ -362,6 +366,10 @@ PJ_DECL(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf, /** * Disconnect unidirectional audio from all sources to the specified sink slot. * + * Note that the operation will be done asynchronously, so application + * should not assume that the port will no longer receive/send audio frame + * after this function has returned. + * * @param conf The conference bridge. * @param sink_slot Sink slot. * @@ -375,6 +383,10 @@ pjmedia_conf_disconnect_port_from_sources( pjmedia_conf *conf, /** * Disconnect unidirectional audio from the specified source to all sink slots. * + * Note that the operation will be done asynchronously, so application + * should not assume that the port will no longer receive/send audio frame + * after this function has returned. + * * @param conf The conference bridge. * @param src_slot Source slot. * @@ -409,6 +421,10 @@ PJ_DECL(unsigned) pjmedia_conf_get_connect_count(pjmedia_conf *conf); /** * Remove the specified port from the conference bridge. * + * Note that the operation will be done asynchronously, so application + * should not assume that the port will no longer receive/send audio frame + * after this function has returned. + * * @param conf The conference bridge. * @param slot The port index to be removed. * diff --git a/pjmedia/include/pjmedia/port.h b/pjmedia/include/pjmedia/port.h index 42eaa138a7..20b1c0e43b 100644 --- a/pjmedia/include/pjmedia/port.h +++ b/pjmedia/include/pjmedia/port.h @@ -416,7 +416,8 @@ typedef struct pjmedia_port pjmedia_frame *frame); /** - * Called to destroy this port. + * Destructor. + * This should only be called by #pjmedia_port_destroy(). */ pj_status_t (*on_destroy)(struct pjmedia_port *this_port); @@ -512,14 +513,21 @@ PJ_DECL(pj_status_t) pjmedia_port_put_frame( pjmedia_port *port, PJ_DECL(pj_status_t) pjmedia_port_destroy( pjmedia_port *port ); + +/* + ******************************************************************* + * Helper functions. + ******************************************************************* + */ + /** * This is a helper function to initialize the port's group lock. This * function will create a group lock if NULL is passed, initialize the group * lock by adding the port's destructor to the group lock handler list, and * increment the reference counter. * - * This function should only be called by a media port implementation and - * after port's on_destroy() function has been assigned. + * This function should only be called by media port implementation. The port + * must have its own pool which will be released in its on_destroy() function. * * @param port The pjmedia port to be initialized. * @param pool The pool, this can be a temporary pool as diff --git a/pjmedia/include/pjmedia/splitcomb.h b/pjmedia/include/pjmedia/splitcomb.h index c9e15be22c..d0b8165edf 100644 --- a/pjmedia/include/pjmedia/splitcomb.h +++ b/pjmedia/include/pjmedia/splitcomb.h @@ -106,7 +106,8 @@ PJ_DECL(pj_status_t) pjmedia_splitcomb_set_channel(pjmedia_port *splitcomb, * media port. So this effectively reverse the phase of the media port. * * @param pool The pool to allocate memory for the port and - * buffers. + * buffers. This is deprecated, the channel will be + * created using splitter/combiner's pool. * @param splitcomb The splitter/combiner. * @param ch_num Audio channel starting number (zero based). * @param options Normally is zero, but the lower 8-bit of the diff --git a/pjmedia/src/pjmedia/avi_player.c b/pjmedia/src/pjmedia/avi_player.c index d22934a545..f0fe2e9922 100644 --- a/pjmedia/src/pjmedia/avi_player.c +++ b/pjmedia/src/pjmedia/avi_player.c @@ -117,6 +117,7 @@ static avi_fmt_info avi_fmts[] = struct pjmedia_avi_streams { + pj_pool_t *pool; unsigned num_streams; pjmedia_port **streams; }; @@ -142,12 +143,12 @@ struct avi_reader_port void (*cb2)(pjmedia_port*, void*); }; - static pj_status_t avi_get_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t avi_on_destroy(pjmedia_port *this_port); -static struct avi_reader_port *create_avi_port(pj_pool_t *pool) +static struct avi_reader_port *create_avi_port(pj_pool_t *pool, + pj_grp_lock_t *grp_lock) { const pj_str_t name = pj_str("file"); struct avi_reader_port *port; @@ -166,6 +167,8 @@ static struct avi_reader_port *create_avi_port(pj_pool_t *pool) port->base.get_frame = &avi_get_frame; port->base.on_destroy = &avi_on_destroy; + pjmedia_port_init_grp_lock(&port->base, pool, grp_lock); + return port; } @@ -196,11 +199,19 @@ static pj_status_t file_read3(pj_oshandle_t fd, void *data, pj_ssize_t size, return status; } + +static void streams_on_destroy(void *arg) +{ + pjmedia_avi_streams *streams = (pjmedia_avi_streams*)arg; + pj_pool_safe_release(&streams->pool); +} + + /* * Create AVI player port. */ PJ_DEF(pj_status_t) -pjmedia_avi_player_create_streams(pj_pool_t *pool, +pjmedia_avi_player_create_streams(pj_pool_t *pool_, const char *filename, unsigned options, pjmedia_avi_streams **p_streams) @@ -209,20 +220,36 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool, struct avi_reader_port *fport[PJMEDIA_AVI_MAX_NUM_STREAMS]; pj_off_t pos; unsigned i, nstr = 0; + pj_pool_t *pool = NULL; + pj_grp_lock_t *grp_lock = NULL; pj_status_t status = PJ_SUCCESS; /* Check arguments. */ - PJ_ASSERT_RETURN(pool && filename && p_streams, PJ_EINVAL); + PJ_ASSERT_RETURN(pool_ && filename && p_streams, PJ_EINVAL); /* Check the file really exists. */ if (!pj_file_exists(filename)) { return PJ_ENOTFOUND; } + /* Create own pool */ + pool = pj_pool_create(pool_->factory, "aviplayer", 500, 500, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + + /* Create group lock */ + status = pj_grp_lock_create(pool, NULL, &grp_lock); + if (status != PJ_SUCCESS) + goto on_error; + /* Create fport instance. */ - fport[0] = create_avi_port(pool); + fport[0] = create_avi_port(pool, grp_lock); if (!fport[0]) { - return PJ_ENOMEM; + /* Destroy group lock here to avoid leak */ + pj_grp_lock_destroy(grp_lock); + grp_lock = NULL; + + status = PJ_ENOMEM; + goto on_error; } /* Get the file size. */ @@ -232,14 +259,15 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool, if (fport[0]->fsize <= (pj_off_t)(sizeof(riff_hdr_t) + sizeof(avih_hdr_t) + sizeof(strl_hdr_t))) { - return PJMEDIA_EINVALIMEDIATYPE; + status = PJMEDIA_EINVALIMEDIATYPE; + goto on_error; } /* Open file. */ status = pj_file_open(pool, filename, PJ_O_RDONLY | PJ_O_CLOEXEC, &fport[0]->fd); if (status != PJ_SUCCESS) - return status; + goto on_error; /* Read the RIFF + AVIH header. */ status = file_read(fport[0]->fd, &avi_hdr, @@ -435,7 +463,7 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool, if (nstr > 0) { /* Create fport instance. */ - fport[nstr] = create_avi_port(pool); + fport[nstr] = create_avi_port(pool, grp_lock); if (!fport[nstr]) { status = PJ_ENOMEM; goto on_error; @@ -542,6 +570,13 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool, for (i = 0; i < nstr; i++) (*p_streams)->streams[i] = &fport[i]->base; + status = pj_grp_lock_add_handler(grp_lock, NULL, *p_streams, + &streams_on_destroy); + if (status != PJ_SUCCESS) + goto on_error; + + (*p_streams)->pool = pool; + PJ_LOG(4,(THIS_FILE, "AVI file player '%.*s' created with " "%d media ports", @@ -552,9 +587,13 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool, return PJ_SUCCESS; on_error: - fport[0]->base.on_destroy(&fport[0]->base); - for (i = 1; i < nstr; i++) - fport[i]->base.on_destroy(&fport[i]->base); + if (grp_lock) { + pjmedia_port_destroy(&fport[0]->base); + for (i = 1; i < nstr; i++) + pjmedia_port_destroy(&fport[i]->base); + } + pj_pool_release(pool); + if (status == AVI_EOF) return PJMEDIA_EINVALIMEDIATYPE; return status; diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c index 28d1d5b042..ba3e45dbff 100644 --- a/pjmedia/src/pjmedia/conference.c +++ b/pjmedia/src/pjmedia/conference.c @@ -110,6 +110,7 @@ static FILE *fhnd_rec; */ struct conf_port { + pj_pool_t *pool; /**< Pool. */ pj_str_t name; /**< Port name. */ pjmedia_port *port; /**< get_frame() and put_frame() */ pjmedia_port_op rx_setting; /**< Can we receive from this port */ @@ -221,14 +222,22 @@ struct conf_port * Burst and drift are handled by delay buffer. */ pjmedia_delay_buf *delay_buf; + + pj_bool_t is_new; /**< Newly added port, avoid read/write + data from/to. */ }; +/* Forward declarations */ +typedef struct op_entry op_entry; + + /* * Conference bridge. */ struct pjmedia_conf { + pj_pool_t *pool; /**< Pool */ unsigned options; /**< Bitmask options. */ unsigned max_ports; /**< Maximum ports. */ unsigned port_cnt; /**< Current number of ports. */ @@ -242,6 +251,9 @@ struct pjmedia_conf unsigned channel_count;/**< Number of channels (1=mono). */ unsigned samples_per_frame; /**< Samples per frame. */ unsigned bits_per_sample; /**< Bits per sample. */ + + op_entry *op_queue; /**< Queue of operations. */ + op_entry *op_queue_free;/**< Queue of free entries. */ }; @@ -259,21 +271,153 @@ static pj_status_t destroy_port_pasv(pjmedia_port *this_port); #endif +/* As we don't hold mutex in the clock/get_frame(), some conference operations + * that change conference states need to be synchronized with the clock. + * So some steps of the operations needs to be executed within the clock tick + * context, especially the steps related to changing ports connection. + */ + +/* Synchronized operation type enumeration. */ +typedef enum op_type +{ + OP_UNKNOWN, + OP_REMOVE_PORT, + OP_CONNECT_PORTS, + OP_DISCONNECT_PORTS, + OP_DISCONNECT_PORT_FROM_SOURCES, + OP_DISCONNECT_PORT_FROM_SINKS, +} op_type; + +/* Synchronized operation parameter. */ +typedef union op_param +{ + struct { + unsigned port; + } remove_port; + + struct { + unsigned src; + unsigned sink; + int adj_level; + } connect_ports; + + struct { + unsigned src; + unsigned sink; + } disconnect_ports; + +} op_param; + +/* Synchronized operation list entry. */ +typedef struct op_entry { + PJ_DECL_LIST_MEMBER(struct op_entry); + op_type type; + op_param param; +} op_entry; + +/* Prototypes of synchronized operation */ +static void op_remove_port(pjmedia_conf *conf, const op_param *prm); +static void op_connect_ports(pjmedia_conf *conf, const op_param *prm); +static void op_disconnect_ports(pjmedia_conf *conf, const op_param *prm); + +static op_entry* get_free_op_entry(pjmedia_conf *conf) +{ + op_entry *ope = NULL; + + /* Get from empty list if any, otherwise, allocate a new one */ + if (!pj_list_empty(conf->op_queue_free)) { + ope = conf->op_queue_free->next; + pj_list_erase(ope); + } else { + ope = PJ_POOL_ZALLOC_T(conf->pool, op_entry); + } + return ope; +} + +static void handle_op_queue(pjmedia_conf *conf) +{ + op_entry *op, *next_op; + + op = conf->op_queue->next; + while (op != conf->op_queue) { + next_op = op->next; + pj_list_erase(op); + + switch(op->type) { + case OP_REMOVE_PORT: + op_remove_port(conf, &op->param); + break; + case OP_CONNECT_PORTS: + op_connect_ports(conf, &op->param); + break; + case OP_DISCONNECT_PORTS: + case OP_DISCONNECT_PORT_FROM_SOURCES: + case OP_DISCONNECT_PORT_FROM_SINKS: + op_disconnect_ports(conf, &op->param); + break; + default: + pj_assert(!"Invalid sync-op in conference"); + break; + } + + op->type = OP_UNKNOWN; + pj_list_push_back(conf->op_queue_free, op); + op = next_op; + } +} + + +/* Group lock handler */ +static void conf_port_on_destroy(void *arg) +{ + struct conf_port *conf_port = (struct conf_port*)arg; + if (conf_port->pool) + pj_pool_safe_release(&conf_port->pool); +} + + /* * Create port. */ -static pj_status_t create_conf_port( pj_pool_t *pool, +static pj_status_t create_conf_port( pj_pool_t *parent_pool, pjmedia_conf *conf, pjmedia_port *port, const pj_str_t *name, struct conf_port **p_conf_port) { struct conf_port *conf_port; - pj_status_t status; + pj_pool_t *pool = NULL; + pj_status_t status = PJ_SUCCESS; + + /* Create own pool */ + pool = pj_pool_create(parent_pool->factory, name->ptr, 500, 500, NULL); + if (!pool) { + status = PJ_ENOMEM; + goto on_return; + } /* Create port. */ conf_port = PJ_POOL_ZALLOC_T(pool, struct conf_port); - PJ_ASSERT_RETURN(conf_port, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(conf_port, {status = PJ_ENOMEM; goto on_return;}); + conf_port->pool = pool; + + /* Increment port ref count to avoid premature destroy due to + * asynchronous port removal. + */ + if (port) { + if (!port->grp_lock) { + /* Create group lock if it does not have one */ + pjmedia_port_init_grp_lock(port, pool, NULL); + } + + pj_grp_lock_add_ref(port->grp_lock); + + /* Pool may be used for creating port's group lock and the group lock + * may be used by app, so pool destroy must be done via handler. + */ + status = pj_grp_lock_add_handler(port->grp_lock, NULL, conf_port, + &conf_port_on_destroy); + } /* Set name */ pj_strdup_with_null(pool, &conf_port->name, name); @@ -289,12 +433,14 @@ static pj_status_t create_conf_port( pj_pool_t *pool, /* Create transmit flag array */ conf_port->listener_slots = (SLOT_TYPE*) pj_pool_zalloc(pool, conf->max_ports * sizeof(SLOT_TYPE)); - PJ_ASSERT_RETURN(conf_port->listener_slots, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(conf_port->listener_slots, + {status = PJ_ENOMEM; goto on_return;}); /* Create adjustment level array */ conf_port->listener_adj_level = (unsigned *) pj_pool_zalloc(pool, conf->max_ports * sizeof(unsigned)); - PJ_ASSERT_RETURN(conf_port->listener_adj_level, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(conf_port->listener_adj_level, + {status = PJ_ENOMEM; goto on_return;}); /* Save some port's infos, for convenience. */ if (port) { @@ -315,6 +461,8 @@ static pj_status_t create_conf_port( pj_pool_t *pool, /* Create adjustment level buffer. */ conf_port->adj_level_buf = (pj_int16_t*) pj_pool_zalloc(pool, conf->samples_per_frame * sizeof(pj_int16_t)); + PJ_ASSERT_ON_FAIL(conf_port->adj_level_buf, + {status = PJ_ENOMEM; goto on_return;}); /* If port's clock rate is different than conference's clock rate, * create a resample sessions. @@ -339,7 +487,7 @@ static pj_status_t create_conf_port( pj_pool_t *pool, conf->clock_rate, &conf_port->rx_resample); if (status != PJ_SUCCESS) - return status; + goto on_return; /* Create resample for tx buffer. */ @@ -352,7 +500,7 @@ static pj_status_t create_conf_port( pj_pool_t *pool, conf->samples_per_frame, &conf_port->tx_resample); if (status != PJ_SUCCESS) - return status; + goto on_return; } /* @@ -403,7 +551,8 @@ static pj_status_t create_conf_port( pj_pool_t *pool, conf_port->rx_buf = (pj_int16_t*) pj_pool_alloc(pool, conf_port->rx_buf_cap * sizeof(conf_port->rx_buf[0])); - PJ_ASSERT_RETURN(conf_port->rx_buf, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(conf_port->rx_buf, + {status = PJ_ENOMEM; goto on_return;}); /* Create TX buffer. */ conf_port->tx_buf_cap = conf_port->rx_buf_cap; @@ -411,7 +560,8 @@ static pj_status_t create_conf_port( pj_pool_t *pool, conf_port->tx_buf = (pj_int16_t*) pj_pool_alloc(pool, conf_port->tx_buf_cap * sizeof(conf_port->tx_buf[0])); - PJ_ASSERT_RETURN(conf_port->tx_buf, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(conf_port->tx_buf, + {status = PJ_ENOMEM; goto on_return;}); } @@ -419,13 +569,21 @@ static pj_status_t create_conf_port( pj_pool_t *pool, conf_port->mix_buf = (pj_int32_t*) pj_pool_zalloc(pool, conf->samples_per_frame * sizeof(conf_port->mix_buf[0])); - PJ_ASSERT_RETURN(conf_port->mix_buf, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(conf_port->mix_buf, + {status = PJ_ENOMEM; goto on_return;}); conf_port->last_mix_adj = NORMAL_LEVEL; /* Done */ *p_conf_port = conf_port; - return PJ_SUCCESS; + +on_return: + if (status != PJ_SUCCESS) { + if (pool) + pj_pool_release(pool); + } + + return status; } @@ -538,7 +696,7 @@ static pj_status_t create_sound_port( pj_pool_t *pool, /* * Create conference bridge. */ -PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool_, unsigned max_ports, unsigned clock_rate, unsigned channel_count, @@ -547,6 +705,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool, unsigned options, pjmedia_conf **p_conf ) { + pj_pool_t *pool; pjmedia_conf *conf; const pj_str_t name = { "Conf", 4 }; pj_status_t status; @@ -558,9 +717,17 @@ PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool, PJ_LOG(5,(THIS_FILE, "Creating conference bridge with %d ports", max_ports)); + /* Create own pool */ + pool = pj_pool_create(pool_->factory, name.ptr, 512, 512, NULL); + if (!pool) { + PJ_PERROR(1, (THIS_FILE, PJ_ENOMEM, "Create failed in alloc")); + return PJ_ENOMEM; + } + /* Create and init conf structure. */ conf = PJ_POOL_ZALLOC_T(pool, pjmedia_conf); PJ_ASSERT_RETURN(conf, PJ_ENOMEM); + conf->pool = pool; conf->ports = (struct conf_port**) pj_pool_zalloc(pool, max_ports*sizeof(void*)); @@ -617,6 +784,17 @@ PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool, } + /* Allocate synchronized operation queues */ + conf->op_queue = PJ_POOL_ZALLOC_T(pool, op_entry); + conf->op_queue_free = PJ_POOL_ZALLOC_T(pool, op_entry); + if (!conf->op_queue || !conf->op_queue_free) { + PJ_PERROR(1, (THIS_FILE, PJ_ENOMEM, "Create failed in create queues")); + pjmedia_conf_destroy(conf); + return PJ_ENOMEM; + } + pj_list_init(conf->op_queue); + pj_list_init(conf->op_queue_free); + /* Done */ *p_conf = conf; @@ -651,37 +829,27 @@ static pj_status_t resume_sound( pjmedia_conf *conf ) */ PJ_DEF(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf ) { - unsigned i, ci; + unsigned i; PJ_ASSERT_RETURN(conf != NULL, PJ_EINVAL); + pj_log_push_indent(); + /* Destroy sound device port. */ if (conf->snd_dev_port) { pjmedia_snd_port_destroy(conf->snd_dev_port); conf->snd_dev_port = NULL; } - /* Destroy delay buf of all (passive) ports. */ - for (i=0, ci=0; imax_ports && ciport_cnt; ++i) { - struct conf_port *cport; + /* Flush any pending operation (connect, disconnect, etc) */ + handle_op_queue(conf); - cport = conf->ports[i]; - if (!cport) - continue; - - ++ci; - - if (cport->rx_resample) { - pjmedia_resample_destroy(cport->rx_resample); - cport->rx_resample = NULL; - } - if (cport->tx_resample) { - pjmedia_resample_destroy(cport->tx_resample); - cport->tx_resample = NULL; - } - if (cport->delay_buf) { - pjmedia_delay_buf_destroy(cport->delay_buf); - cport->delay_buf = NULL; + /* Remove all ports (may destroy them too). */ + for (i=0; imax_ports; ++i) { + if (conf->ports[i]) { + op_param oprm = {0}; + oprm.remove_port.port = i; + op_remove_port(conf, &oprm); } } @@ -689,6 +857,12 @@ PJ_DEF(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf ) if (conf->mutex) pj_mutex_destroy(conf->mutex); + /* Destroy pool */ + if (conf->pool) + pj_pool_safe_release(&conf->pool); + + pj_log_pop_indent(); + return PJ_SUCCESS; } @@ -770,10 +944,12 @@ PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf, { struct conf_port *conf_port; unsigned index; - pj_status_t status; + pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(conf && pool && strm_port, PJ_EINVAL); + pj_log_push_indent(); + /* If port_name is not specified, use the port's name */ if (!port_name) port_name = &strm_port->info.name; @@ -787,44 +963,52 @@ PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf, conf->channel_count != 1)) { pj_assert(!"Number of channels mismatch"); - return PJMEDIA_ENCCHANNEL; + status = PJMEDIA_ENCCHANNEL; + goto on_return; } pj_mutex_lock(conf->mutex); - if (conf->port_cnt >= conf->max_ports) { - pj_assert(!"Too many ports"); - pj_mutex_unlock(conf->mutex); - return PJ_ETOOMANY; - } - /* Find empty port in the conference bridge. */ for (index=0; index < conf->max_ports; ++index) { if (conf->ports[index] == NULL) break; } - pj_assert(index != conf->max_ports); + if (index == conf->max_ports) { + PJ_PERROR(3,(THIS_FILE, PJ_ETOOMANY, "Add port %s failed", + port_name->ptr)); + status = PJ_ETOOMANY; + goto on_return; + } /* Create conf port structure. */ status = create_conf_port(pool, conf, strm_port, port_name, &conf_port); - if (status != PJ_SUCCESS) { - pj_mutex_unlock(conf->mutex); - return status; - } + if (status != PJ_SUCCESS) + goto on_return; - /* Put the port. */ + /* Audio data flow is not protected, avoid processing this newly + * added port. + */ + conf_port->is_new = PJ_TRUE; + + /* Put the port, but don't add port counter yet */ conf->ports[index] = conf_port; - conf->port_cnt++; + //conf->port_cnt++; /* Done. */ if (p_port) { *p_port = index; } + PJ_LOG(5,(THIS_FILE, "Adding new port %d (%.*s)", + index, (int)port_name->slen, port_name->ptr)); + +on_return: pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); - return PJ_SUCCESS; + return status; } @@ -983,7 +1167,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf, unsigned i; /* Check arguments */ - PJ_ASSERT_RETURN(conf && src_slotmax_ports && + PJ_ASSERT_RETURN(conf && src_slotmax_ports && sink_slotmax_ports, PJ_EINVAL); /* Value must be from -128 to +127 */ @@ -992,43 +1176,52 @@ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf, */ PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL); + pj_log_push_indent(); + + PJ_LOG(5,(THIS_FILE, "Connect ports %d->%d requested", + src_slot, sink_slot)); + pj_mutex_lock(conf->mutex); /* Ports must be valid. */ src_port = conf->ports[src_slot]; dst_port = conf->ports[sink_slot]; if (!src_port || !dst_port) { + PJ_PERROR(3,(THIS_FILE, PJ_EINVAL, + "Failed connecting ports, make sure ports are valid")); pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); return PJ_EINVAL; } /* Check if connection has been made */ for (i=0; ilistener_cnt; ++i) { - if (src_port->listener_slots[i] == sink_slot) + if (src_port->listener_slots[i] == sink_slot) { + PJ_LOG(3,(THIS_FILE, "Ports connection %d->%d already exists", + src_slot, sink_slot)); break; + } } + /* Queue the operation */ if (i == src_port->listener_cnt) { - src_port->listener_slots[src_port->listener_cnt] = sink_slot; - /* Set normalized adjustment level. */ - src_port->listener_adj_level[src_port->listener_cnt] = adj_level + - NORMAL_LEVEL; - ++conf->connect_cnt; - ++src_port->listener_cnt; - ++dst_port->transmitter_cnt; - - if (conf->connect_cnt == 1) - start_sound = 1; - - PJ_LOG(4,(THIS_FILE,"Port %d (%.*s) transmitting to port %d (%.*s)", - src_slot, - (int)src_port->name.slen, - src_port->name.ptr, - sink_slot, - (int)dst_port->name.slen, - dst_port->name.ptr)); + op_entry *ope; + + ope = get_free_op_entry(conf); + ope->type = OP_CONNECT_PORTS; + ope->param.connect_ports.src = src_slot; + ope->param.connect_ports.sink = sink_slot; + ope->param.connect_ports.adj_level = adj_level; + pj_list_push_back(conf->op_queue, ope); + + PJ_LOG(4,(THIS_FILE, "Connect ports %d->%d queued", + src_slot, sink_slot)); } + /* This is first connection, start clock */ + if (conf->connect_cnt == 0) + start_sound = 1; + pj_mutex_unlock(conf->mutex); /* Sound device must be started without mutex, otherwise the @@ -1037,9 +1230,56 @@ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf, if (start_sound) resume_sound(conf); + pj_log_pop_indent(); + return PJ_SUCCESS; } +static void op_connect_ports(pjmedia_conf *conf, const op_param *prm) +{ + unsigned src_slot, sink_slot; + struct conf_port *src_port, *dst_port; + unsigned i; + + /* Ports must be valid. */ + src_slot = prm->connect_ports.src; + sink_slot = prm->connect_ports.sink; + src_port = conf->ports[src_slot]; + dst_port = conf->ports[sink_slot]; + + if (!src_port || !dst_port) { + PJ_PERROR(3,(THIS_FILE, PJ_EINVAL, + "Failed connecting ports, make sure ports are valid")); + return; + } + + /* Check if connection has been made */ + for (i=0; ilistener_cnt; ++i) { + if (src_port->listener_slots[i] == sink_slot) { + PJ_LOG(3,(THIS_FILE, "Ports connection %d->%d already exists", + src_slot, sink_slot)); + return; + } + } + + src_port->listener_slots[src_port->listener_cnt] = sink_slot; + + /* Set normalized adjustment level. */ + src_port->listener_adj_level[src_port->listener_cnt] = + prm->connect_ports.adj_level + NORMAL_LEVEL; + + ++conf->connect_cnt; + ++src_port->listener_cnt; + ++dst_port->transmitter_cnt; + + PJ_LOG(4,(THIS_FILE,"Port %d (%.*s) transmitting to port %d (%.*s)", + src_slot, + (int)src_port->name.slen, + src_port->name.ptr, + sink_slot, + (int)dst_port->name.slen, + dst_port->name.ptr)); +} /* * Disconnect port @@ -1049,20 +1289,26 @@ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf, unsigned sink_slot ) { struct conf_port *src_port, *dst_port; - pj_bool_t no_conn = PJ_FALSE; unsigned i; /* Check arguments */ - PJ_ASSERT_RETURN(conf && src_slotmax_ports && + PJ_ASSERT_RETURN(conf && src_slotmax_ports && sink_slotmax_ports, PJ_EINVAL); + pj_log_push_indent(); + + PJ_LOG(5,(THIS_FILE, "Disconnect ports %d->%d requested", + src_slot, sink_slot)); + pj_mutex_lock(conf->mutex); /* Ports must be valid. */ src_port = conf->ports[src_slot]; dst_port = conf->ports[sink_slot]; if (!src_port || !dst_port) { + PJ_PERROR(3,(THIS_FILE, PJ_EINVAL,"Cannot disconnect invalid ports")); pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); return PJ_EINVAL; } @@ -1072,12 +1318,62 @@ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf, break; } - if (i != src_port->listener_cnt) { - pj_assert(src_port->listener_cnt > 0 && + if (i == src_port->listener_cnt) { + PJ_LOG(3,(THIS_FILE, "Ports connection %d->%d does not exist", + src_slot, sink_slot)); + } else { + op_entry *ope; + + ope = get_free_op_entry(conf); + ope->type = OP_DISCONNECT_PORTS; + ope->param.disconnect_ports.src = src_slot; + ope->param.disconnect_ports.sink = sink_slot; + pj_list_push_back(conf->op_queue, ope); + + PJ_LOG(4,(THIS_FILE, "Disconnect ports %d->%d queued", + src_slot, sink_slot)); + } + + pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); + + return PJ_SUCCESS; +} + +static void op_disconnect_ports(pjmedia_conf *conf, + const op_param *prm) +{ + unsigned src_slot, sink_slot; + struct conf_port *src_port = NULL, *dst_port = NULL; + unsigned i; + + /* Ports must be valid. */ + src_slot = prm->disconnect_ports.src; + sink_slot = prm->disconnect_ports.sink; + + if (src_slot != INVALID_SLOT) + src_port = conf->ports[src_slot]; + if (sink_slot != INVALID_SLOT) + dst_port = conf->ports[sink_slot]; + + /* Disconnect source -> sink */ + if (src_port && dst_port) { + /* Check if connection has been made */ + for (i=0; ilistener_cnt; ++i) { + if (src_port->listener_slots[i] == sink_slot) + break; + } + if (i == src_port->listener_cnt) { + PJ_LOG(3,(THIS_FILE, "Ports connection %d->%d does not exist", + src_slot, sink_slot)); + return; + } + + pj_assert(src_port->listener_cnt > 0 && src_port->listener_cnt < conf->max_ports); - pj_assert(dst_port->transmitter_cnt > 0 && + pj_assert(dst_port->transmitter_cnt > 0 && dst_port->transmitter_cnt < conf->max_ports); - pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE), + pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE), src_port->listener_cnt, i); pj_array_erase(src_port->listener_adj_level, sizeof(unsigned), src_port->listener_cnt, i); @@ -1094,24 +1390,68 @@ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf, (int)dst_port->name.slen, dst_port->name.ptr)); - /* if source port is passive port and has no listener, reset delaybuf */ + /* if source port is passive port and has no listener, + * reset delaybuf. + */ if (src_port->delay_buf && src_port->listener_cnt == 0) pjmedia_delay_buf_reset(src_port->delay_buf); - } - /* Evaluate connect_cnt with mutex, but pause sound dev outside mutex */ - no_conn = (conf->connect_cnt == 0); + /* Disconnect multiple conn: any -> sink */ + } else if (dst_port) { + PJ_LOG(4,(THIS_FILE, + "Stop any transmission to port %d (%.*s)", + sink_slot, + (int)dst_port->name.slen, + dst_port->name.ptr)); - pj_mutex_unlock(conf->mutex); + for (i=0; imax_ports; ++i) { + unsigned j; - if (no_conn) { - pause_sound(conf); + src_port = conf->ports[i]; + if (!src_port || src_port->listener_cnt == 0) + continue; + + for (j=0; jlistener_cnt; ++j) { + if (src_port->listener_slots[j] == sink_slot) { + op_param op_prm = {0}; + op_prm.disconnect_ports.src = i; + op_prm.disconnect_ports.sink = sink_slot; + op_disconnect_ports(conf, &op_prm); + break; + } + } + } + + /* Disconnect multiple conn: source -> any */ + } else if (src_port) { + PJ_LOG(4,(THIS_FILE, + "Stop any transmission from port %d (%.*s)", + src_slot, + (int)src_port->name.slen, + src_port->name.ptr)); + + for (i=0; ilistener_cnt; ++i) { + op_param op_prm = {0}; + op_prm.disconnect_ports.src = src_slot; + op_prm.disconnect_ports.sink = src_port->listener_slots[i]; + op_disconnect_ports(conf, &op_prm); + } + + /* Invalid ports */ + } else { + pj_assert(!"Invalid ports specified in conf disconnect"); } - return PJ_SUCCESS; + /* Pause sound dev when there is no connection, the pause should be done + * outside mutex to avoid possible deadlock. + * Note that currently this is done with mutex, it is safe because + * pause_sound() is a no-op (just maintaining old code). + */ + if (conf->connect_cnt == 0) { + pause_sound(conf); + } } - /* * Disconnect port from all sources */ @@ -1119,45 +1459,43 @@ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port_from_sources( pjmedia_conf *conf, unsigned sink_slot) { - unsigned i; + struct conf_port *dst_port; /* Check arguments */ PJ_ASSERT_RETURN(conf && sink_slotmax_ports, PJ_EINVAL); - pj_mutex_lock(conf->mutex); + pj_log_push_indent(); + PJ_LOG(5,(THIS_FILE, "Disconnect ports any->%d requested", + sink_slot)); - /* Remove this port from transmit array of other ports. */ - for (i=0; imax_ports; ++i) { - unsigned j; - struct conf_port *src_port; - - src_port = conf->ports[i]; + pj_mutex_lock(conf->mutex); - if (!src_port) - continue; + /* Ports must be valid. */ + dst_port = conf->ports[sink_slot]; + if (!dst_port) { + PJ_PERROR(3,(THIS_FILE, PJ_EINVAL,"Cannot disconnect invalid port")); + pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); + return PJ_EINVAL; + } - if (src_port->listener_cnt == 0) - continue; + if (dst_port->transmitter_cnt == 0) { + PJ_LOG(3,(THIS_FILE, "Port %d does not have any transmitter", + sink_slot)); + } else { + op_entry *ope; - for (j=0; jlistener_cnt; ++j) { - if (src_port->listener_slots[j] == sink_slot) { - pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE), - src_port->listener_cnt, j); - pj_array_erase(src_port->listener_adj_level, sizeof(unsigned), - src_port->listener_cnt, j); - pj_assert(conf->connect_cnt > 0); - --conf->connect_cnt; - --src_port->listener_cnt; - break; - } - } - } + ope = get_free_op_entry(conf); + ope->type = OP_DISCONNECT_PORTS; + ope->param.disconnect_ports.src = INVALID_SLOT; + ope->param.disconnect_ports.sink = sink_slot; + pj_list_push_back(conf->op_queue, ope); - if (conf->connect_cnt == 0) { - pause_sound(conf); + PJ_LOG(4,(THIS_FILE, "Disconnect ports any->%d queued", sink_slot)); } pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); return PJ_SUCCESS; } @@ -1175,33 +1513,38 @@ pjmedia_conf_disconnect_port_from_sinks( pjmedia_conf *conf, /* Check arguments */ PJ_ASSERT_RETURN(conf && src_slotmax_ports, PJ_EINVAL); + pj_log_push_indent(); + + PJ_LOG(5,(THIS_FILE, "Disconnect ports %d->any requested", + src_slot)); + pj_mutex_lock(conf->mutex); /* Port must be valid. */ src_port = conf->ports[src_slot]; if (!src_port) { + PJ_PERROR(3,(THIS_FILE, PJ_EINVAL,"Cannot disconnect invalid port")); pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } - /* Update transmitter_cnt of ports we're transmitting to */ - while (src_port->listener_cnt) { - unsigned dst_slot; - struct conf_port *dst_port; + if (src_port->listener_cnt == 0) { + PJ_LOG(3,(THIS_FILE, "Port %d does not have any transmitter", + src_slot)); + } else { + op_entry *ope; - dst_slot = src_port->listener_slots[src_port->listener_cnt-1]; - dst_port = conf->ports[dst_slot]; - --dst_port->transmitter_cnt; - --src_port->listener_cnt; - pj_assert(conf->connect_cnt > 0); - --conf->connect_cnt; - } + ope = get_free_op_entry(conf); + ope->type = OP_DISCONNECT_PORTS; + ope->param.disconnect_ports.src = src_slot; + ope->param.disconnect_ports.sink = INVALID_SLOT; + pj_list_push_back(conf->op_queue, ope); - if (conf->connect_cnt == 0) { - pause_sound(conf); + PJ_LOG(4,(THIS_FILE, "Disconnect ports %d->any queued", src_slot)); } pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); return PJ_SUCCESS; } @@ -1231,32 +1574,67 @@ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf, unsigned port ) { struct conf_port *conf_port; + op_entry *ope; - /* Check arguments */ - PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL); + pj_log_push_indent(); - /* Suspend the sound devices. - * Don't want to remove port while port is being accessed by sound - * device's threads! - */ + PJ_LOG(5,(THIS_FILE, "Port %d remove requested", port)); + + PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL); pj_mutex_lock(conf->mutex); /* Port must be valid. */ conf_port = conf->ports[port]; if (conf_port == NULL) { + PJ_PERROR(3, (THIS_FILE, PJ_EINVAL, "Remove port failed")); pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); return PJ_EINVAL; } + /* Queue the operation */ + ope = get_free_op_entry(conf); + ope->type = OP_REMOVE_PORT; + ope->param.remove_port.port = port; + pj_list_push_back(conf->op_queue, ope); + PJ_LOG(4,(THIS_FILE, "Port %d (%.*s) remove queued", port, + (int)conf_port->name.slen, conf_port->name.ptr)); + + pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); + + return PJ_SUCCESS; +} + + +static void op_remove_port(pjmedia_conf *conf, const op_param *prm) +{ + unsigned port = prm->remove_port.port; + struct conf_port *conf_port; + op_param op_prm; + + /* Port must be valid. */ + conf_port = conf->ports[port]; + if (conf_port == NULL) { + PJ_PERROR(3, (THIS_FILE, PJ_ENOTFOUND, "Remove port failed")); + return; + } + conf_port->tx_setting = PJMEDIA_PORT_DISABLE; conf_port->rx_setting = PJMEDIA_PORT_DISABLE; /* disconnect port from all sources which are transmitting to it */ - pjmedia_conf_disconnect_port_from_sources(conf, port); + pj_bzero(&op_prm, sizeof(op_prm)); + op_prm.disconnect_ports.src = INVALID_SLOT; + op_prm.disconnect_ports.sink = port; + op_disconnect_ports(conf, &op_prm); /* disconnect port from all sinks to which it is transmitting to */ - pjmedia_conf_disconnect_port_from_sinks(conf, port); + pj_bzero(&op_prm, sizeof(op_prm)); + op_prm.disconnect_ports.src = port; + op_prm.disconnect_ports.sink = INVALID_SLOT; + op_disconnect_ports(conf, &op_prm); /* Destroy resample if this conf port has it. */ if (conf_port->rx_resample) { @@ -1275,7 +1653,8 @@ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf, pjmedia_delay_buf_destroy(conf_port->delay_buf); conf_port->delay_buf = NULL; - pjmedia_port_destroy(conf_port->port); + if (conf_port->port) + pjmedia_port_destroy(conf_port->port); conf_port->port = NULL; } @@ -1283,9 +1662,14 @@ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf, conf->ports[port] = NULL; --conf->port_cnt; - pj_mutex_unlock(conf->mutex); + PJ_LOG(4,(THIS_FILE,"Removed port %d (%.*s)", + port, (int)conf_port->name.slen, conf_port->name.ptr)); - return PJ_SUCCESS; + /* Decrease conf port ref count */ + if (conf_port->port && conf_port->port->grp_lock) + pj_grp_lock_dec_ref(conf_port->port->grp_lock); + else + conf_port_on_destroy(conf_port); } @@ -1964,8 +2348,38 @@ static pj_status_t get_frame(pjmedia_port *this_port, pj_assert(frame->size == conf->samples_per_frame * conf->bits_per_sample / 8); - /* Must lock mutex */ - pj_mutex_lock(conf->mutex); + /* Perform any queued operations that need to be synchronized with + * the clock such as connect, disonnect, remove. + */ + if (!pj_list_empty(conf->op_queue)) { + pj_log_push_indent(); + pj_mutex_lock(conf->mutex); + + /* Activate any newly added port */ + for (i=0; imax_ports; ++i) { + struct conf_port *port = conf->ports[i]; + if (!port || !port->is_new) + continue; + + port->is_new = PJ_FALSE; + ++conf->port_cnt; + + PJ_LOG(5,(THIS_FILE, "New port %d (%.*s) is added", + i, (int)port->name.slen, port->name.ptr)); + } + + handle_op_queue(conf); + + pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); + } + + /* No mutex from this point! Otherwise it may cause deadlock as + * put_frame()/get_frame() may invoke callback. + * + * Note that any changes on the conference connections must be + * synchronized. + */ /* Reset port source count. We will only reset port's mix * buffer when we have someone transmitting to it. @@ -1973,8 +2387,8 @@ static pj_status_t get_frame(pjmedia_port *this_port, for (i=0, ci=0; imax_ports && ci < conf->port_cnt; ++i) { struct conf_port *conf_port = conf->ports[i]; - /* Skip empty port. */ - if (!conf_port) + /* Skip empty or new port. */ + if (!conf_port || conf_port->is_new) continue; /* Var "ci" is to count how many ports have been visited so far. */ @@ -2001,8 +2415,8 @@ static pj_status_t get_frame(pjmedia_port *this_port, struct conf_port *conf_port = conf->ports[i]; pj_int32_t level = 0; - /* Skip empty port. */ - if (!conf_port) + /* Skip empty or new port. */ + if (!conf_port || conf_port->is_new) continue; /* Var "ci" is to count how many ports have been visited so far. */ @@ -2216,7 +2630,7 @@ static pj_status_t get_frame(pjmedia_port *this_port, pjmedia_frame_type frm_type; pj_status_t status; - if (!conf_port) + if (!conf_port || conf_port->is_new) continue; /* Var "ci" is to count how many ports have been visited. */ @@ -2263,8 +2677,6 @@ static pj_status_t get_frame(pjmedia_port *this_port, /* MUST set frame type */ frame->type = speaker_frame_type; - pj_mutex_unlock(conf->mutex); - #ifdef REC_FILE if (fhnd_rec == NULL) fhnd_rec = fopen(REC_FILE, "wb"); diff --git a/pjmedia/src/pjmedia/echo_port.c b/pjmedia/src/pjmedia/echo_port.c index 5fc24bf556..23741ffefe 100644 --- a/pjmedia/src/pjmedia/echo_port.c +++ b/pjmedia/src/pjmedia/echo_port.c @@ -31,6 +31,7 @@ struct ec { pjmedia_port base; + pj_pool_t *pool; pjmedia_port *dn_port; pjmedia_echo_state *ec; }; @@ -43,7 +44,7 @@ static pj_status_t ec_get_frame(pjmedia_port *this_port, static pj_status_t ec_on_destroy(pjmedia_port *this_port); -PJ_DEF(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool_, pjmedia_port *dn_port, unsigned tail_ms, unsigned latency_ms, @@ -54,16 +55,22 @@ PJ_DEF(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool, pjmedia_audio_format_detail *afd; struct ec *ec; pj_status_t status; + pj_pool_t *pool = NULL; - PJ_ASSERT_RETURN(pool && dn_port && p_port, PJ_EINVAL); + PJ_ASSERT_RETURN(pool_ && dn_port && p_port, PJ_EINVAL); afd = pjmedia_format_get_audio_format_detail(&dn_port->info.fmt, PJ_TRUE); PJ_ASSERT_RETURN(afd->bits_per_sample==16 && tail_ms, PJ_EINVAL); + /* Create own pool */ + pool = pj_pool_create(pool_->factory, AEC.ptr, 500, 500, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + /* Create the port and the AEC itself */ ec = PJ_POOL_ZALLOC_T(pool, struct ec); + ec->pool = pool; pjmedia_port_info_init(&ec->base.info, &AEC, SIGNATURE, afd->clock_rate, @@ -75,8 +82,10 @@ PJ_DEF(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool, afd->channel_count, PJMEDIA_AFD_SPF(afd), tail_ms, latency_ms, options, &ec->ec); - if (status != PJ_SUCCESS) + if (status != PJ_SUCCESS) { + pj_pool_release(pool); return status; + } /* More init */ ec->dn_port = dn_port; @@ -84,6 +93,9 @@ PJ_DEF(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool, ec->base.put_frame = &ec_put_frame; ec->base.on_destroy = &ec_on_destroy; + if (dn_port->grp_lock) + pjmedia_port_init_grp_lock(&ec->base, pool, dn_port->grp_lock); + /* Done */ *p_port = &ec->base; @@ -137,7 +149,13 @@ static pj_status_t ec_on_destroy(pjmedia_port *this_port) PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVAL); - pjmedia_echo_destroy(ec->ec); + if (ec->ec) { + pjmedia_echo_destroy(ec->ec); + ec->ec = NULL; + } + + if (ec->pool) + pj_pool_safe_release(&ec->pool); return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/mem_capture.c b/pjmedia/src/pjmedia/mem_capture.c index d03771c0ad..679011b047 100644 --- a/pjmedia/src/pjmedia/mem_capture.c +++ b/pjmedia/src/pjmedia/mem_capture.c @@ -31,6 +31,7 @@ struct mem_rec { pjmedia_port base; + pj_pool_t *pool; unsigned options; @@ -54,7 +55,7 @@ static pj_status_t rec_get_frame(pjmedia_port *this_port, static pj_status_t rec_on_destroy(pjmedia_port *this_port); -PJ_DEF(pj_status_t) pjmedia_mem_capture_create( pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_mem_capture_create( pj_pool_t *pool_, void *buffer, pj_size_t size, unsigned clock_rate, @@ -66,18 +67,26 @@ PJ_DEF(pj_status_t) pjmedia_mem_capture_create( pj_pool_t *pool, { struct mem_rec *rec; const pj_str_t name = { "memrec", 6 }; + pj_pool_t *pool; /* Sanity check */ - PJ_ASSERT_RETURN(pool && buffer && size && clock_rate && channel_count && + PJ_ASSERT_RETURN(pool_ && buffer && size && clock_rate && channel_count && samples_per_frame && bits_per_sample && p_port, PJ_EINVAL); /* Can only support 16bit PCM */ PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); + /* Create own pool */ + pool = pj_pool_create(pool_->factory, "memcap", 128, 128, NULL); + if (!pool) { + PJ_PERROR(1, (THIS_FILE, PJ_ENOMEM, "Mem capture create failed")); + return PJ_ENOMEM; + } rec = PJ_POOL_ZALLOC_T(pool, struct mem_rec); - PJ_ASSERT_RETURN(rec != NULL, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(rec != NULL, {pj_pool_release(pool); return PJ_ENOMEM;}); + rec->pool = pool; /* Create the rec */ pjmedia_port_info_init(&rec->base.info, &name, SIGNATURE, @@ -298,6 +307,9 @@ static pj_status_t rec_on_destroy(pjmedia_port *this_port) (*rec->cb)(this_port, rec->user_data); } + if (rec->pool) + pj_pool_safe_release(&rec->pool); + return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/mem_player.c b/pjmedia/src/pjmedia/mem_player.c index ff38065df6..1570947522 100644 --- a/pjmedia/src/pjmedia/mem_player.c +++ b/pjmedia/src/pjmedia/mem_player.c @@ -31,6 +31,7 @@ struct mem_player { pjmedia_port base; + pj_pool_t *pool; unsigned options; pj_timestamp timestamp; @@ -55,7 +56,7 @@ static pj_status_t mem_get_frame(pjmedia_port *this_port, static pj_status_t mem_on_destroy(pjmedia_port *this_port); -PJ_DEF(pj_status_t) pjmedia_mem_player_create( pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_mem_player_create( pj_pool_t *pool_, const void *buffer, pj_size_t size, unsigned clock_rate, @@ -67,9 +68,10 @@ PJ_DEF(pj_status_t) pjmedia_mem_player_create( pj_pool_t *pool, { struct mem_player *port; pj_str_t name = pj_str("memplayer"); + pj_pool_t *pool = NULL; /* Sanity check */ - PJ_ASSERT_RETURN(pool && buffer && size && clock_rate && channel_count && + PJ_ASSERT_RETURN(pool_ && buffer && size && clock_rate && channel_count && samples_per_frame && bits_per_sample && p_port, PJ_EINVAL); @@ -77,8 +79,13 @@ PJ_DEF(pj_status_t) pjmedia_mem_player_create( pj_pool_t *pool, PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); + /* Create own pool */ + pool = pj_pool_create(pool_->factory, name.ptr, 500, 500, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + port = PJ_POOL_ZALLOC_T(pool, struct mem_player); - PJ_ASSERT_RETURN(port != NULL, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(port != NULL, {pj_pool_release(pool);return PJ_ENOMEM;}); + port->pool = pool; /* Create the port */ pjmedia_port_info_init(&port->base.info, &name, SIGNATURE, clock_rate, @@ -318,6 +325,9 @@ static pj_status_t mem_on_destroy(pjmedia_port *this_port) /* Destroy signature */ this_port->info.signature = 0; + if (player->pool) + pj_pool_safe_release(&player->pool); + return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/null_port.c b/pjmedia/src/pjmedia/null_port.c index fb5dfccf4d..92bc813faf 100644 --- a/pjmedia/src/pjmedia/null_port.c +++ b/pjmedia/src/pjmedia/null_port.c @@ -25,6 +25,12 @@ #define SIGNATURE PJMEDIA_SIG_PORT_NULL +struct null_port +{ + pjmedia_port base; + pj_pool_t *pool; +}; + static pj_status_t null_get_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t null_put_frame(pjmedia_port *this_port, @@ -32,30 +38,36 @@ static pj_status_t null_put_frame(pjmedia_port *this_port, static pj_status_t null_on_destroy(pjmedia_port *this_port); -PJ_DEF(pj_status_t) pjmedia_null_port_create( pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_null_port_create( pj_pool_t *pool_, unsigned sampling_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_port **p_port ) { - pjmedia_port *port; + struct null_port *port; const pj_str_t name = pj_str("null-port"); + pj_pool_t *pool; + + PJ_ASSERT_RETURN(pool_ && p_port, PJ_EINVAL); - PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL); + /* Create own pool */ + pool = pj_pool_create(pool_->factory, name.ptr, 128, 128, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); - port = PJ_POOL_ZALLOC_T(pool, pjmedia_port); - PJ_ASSERT_RETURN(port != NULL, PJ_ENOMEM); + port = PJ_POOL_ZALLOC_T(pool, struct null_port); + PJ_ASSERT_ON_FAIL(port, {pj_pool_release(pool); return PJ_ENOMEM;}); + port->pool = pool; - pjmedia_port_info_init(&port->info, &name, SIGNATURE, sampling_rate, + pjmedia_port_info_init(&port->base.info, &name, SIGNATURE, sampling_rate, channel_count, bits_per_sample, samples_per_frame); - port->get_frame = &null_get_frame; - port->put_frame = &null_put_frame; - port->on_destroy = &null_on_destroy; + port->base.get_frame = &null_get_frame; + port->base.put_frame = &null_put_frame; + port->base.on_destroy = &null_on_destroy; - *p_port = port; + *p_port = &port->base; return PJ_SUCCESS; } @@ -95,6 +107,10 @@ static pj_status_t null_get_frame(pjmedia_port *this_port, */ static pj_status_t null_on_destroy(pjmedia_port *this_port) { - PJ_UNUSED_ARG(this_port); + struct null_port* port = (struct null_port*) this_port; + + if (port->pool) + pj_pool_safe_release(&port->pool); + return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/port.c b/pjmedia/src/pjmedia/port.c index 605e060bbc..cede0d0688 100644 --- a/pjmedia/src/pjmedia/port.c +++ b/pjmedia/src/pjmedia/port.c @@ -126,8 +126,7 @@ PJ_DEF(pj_status_t) pjmedia_port_destroy( pjmedia_port *port ) PJ_ASSERT_RETURN(port, PJ_EINVAL); if (port->grp_lock) { - pjmedia_port_dec_ref(port); - return PJ_SUCCESS; + return pjmedia_port_dec_ref(port); } if (port->on_destroy) { @@ -160,16 +159,17 @@ PJ_DEF(pj_status_t) pjmedia_port_init_grp_lock( pjmedia_port *port, PJ_ASSERT_RETURN(port && pool, PJ_EINVAL); PJ_ASSERT_RETURN(port->grp_lock == NULL, PJ_EEXISTS); - /* We need to be caution on ports that do not have the on_destroy()! - * It is either uninitialized yet or the port does not have one. - * If the port doesn't have one, we'd expect a possible premature destroy! + /* We need to be caution on ports that do not have its own pool, + * such port is likely using app's pool, so if the app destroys the port + * and then destroys the pool immediately, it may cause crash as the port + * may have not really been destroyed and may still be accessed. + * When port has a pool, it usually implements on_destroy() for releasing + * the pool, so here we check availability of on_destroy implementation. */ if (port->on_destroy == NULL) { - PJ_LOG(3,(THIS_FILE, "Media port %s is using group lock but does not " - "implement on_destroy()!", + PJ_LOG(2,(THIS_FILE, "Warning, media port %s is using group lock, but " + "it does not seem to have a pool.", port->info.name.ptr)); - pj_assert(!"Port using group lock should implement on_destroy()!"); - return PJ_EINVALIDOP; } if (!grp_lock) { @@ -177,14 +177,19 @@ PJ_DEF(pj_status_t) pjmedia_port_init_grp_lock( pjmedia_port *port, status = pj_grp_lock_create_w_handler(pool, NULL, port, &port_on_destroy, &grp_lock); - } else { - /* Just add handler, and use internal group lock pool */ - status = pj_grp_lock_add_handler(grp_lock, NULL, port, - &port_on_destroy); - } - if (status == PJ_SUCCESS) { + /* Add ref */ + if (status == PJ_SUCCESS) + status = pj_grp_lock_add_ref(grp_lock); + } else { + /* Add ref first before add handler */ status = pj_grp_lock_add_ref(grp_lock); + + /* Just add handler, and use internal group lock pool */ + if (status == PJ_SUCCESS) { + status = pj_grp_lock_add_handler(grp_lock, NULL, port, + &port_on_destroy); + } } if (status == PJ_SUCCESS) { diff --git a/pjmedia/src/pjmedia/splitcomb.c b/pjmedia/src/pjmedia/splitcomb.c index fd03ec94d5..b66774a840 100644 --- a/pjmedia/src/pjmedia/splitcomb.c +++ b/pjmedia/src/pjmedia/splitcomb.c @@ -83,6 +83,7 @@ enum sc_dir struct splitcomb { pjmedia_port base; + pj_pool_t *pool; unsigned options; @@ -203,7 +204,7 @@ static pj_status_t rport_on_destroy(pjmedia_port *this_port); /* * Create the splitter/combiner. */ -PJ_DEF(pj_status_t) pjmedia_splitcomb_create( pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_splitcomb_create( pj_pool_t *pool_, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, @@ -212,10 +213,12 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create( pj_pool_t *pool, pjmedia_port **p_splitcomb) { const pj_str_t name = pj_str("splitcomb"); + pj_pool_t *pool = NULL; struct splitcomb *sc; + pj_status_t status; /* Sanity check */ - PJ_ASSERT_RETURN(pool && clock_rate && channel_count && + PJ_ASSERT_RETURN(pool_ && clock_rate && channel_count && samples_per_frame && bits_per_sample && p_splitcomb, PJ_EINVAL); @@ -224,9 +227,14 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create( pj_pool_t *pool, *p_splitcomb = NULL; + /* Create own pool */ + pool = pj_pool_create(pool_->factory, "splitcomb", 500, 500, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + /* Create the splitter/combiner structure */ sc = PJ_POOL_ZALLOC_T(pool, struct splitcomb); - PJ_ASSERT_RETURN(sc != NULL, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(sc != NULL, {pj_pool_release(pool);return PJ_ENOMEM;}); + sc->pool = pool; /* Create temporary buffers */ sc->get_buf = (TMP_SAMP_TYPE*) @@ -253,6 +261,13 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create( pj_pool_t *pool, sc->base.get_frame = &get_frame; sc->base.on_destroy = &on_destroy; + /* Create group lock */ + status = pjmedia_port_init_grp_lock(&sc->base, pool, NULL); + if (status != PJ_SUCCESS) { + pj_pool_release(pool); + return status; + } + /* Init ports array */ /* sc->port_desc = pj_pool_zalloc(pool, channel_count*sizeof(*sc->port_desc)); @@ -291,6 +306,16 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_set_channel( pjmedia_port *splitcomb, sc->port_desc[ch_num].port = port; sc->port_desc[ch_num].reversed = PJ_FALSE; + if (!port->grp_lock) { + /* Create group lock if it does not have one. + * Note: don't use splitcomb's group lock as we will addref it here + * and only decref it from the destructor. + */ + pjmedia_port_init_grp_lock(port, sc->pool, NULL); + } + + pjmedia_port_add_ref(port); + return PJ_SUCCESS; } @@ -314,7 +339,8 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool, pj_status_t status; /* Sanity check */ - PJ_ASSERT_RETURN(pool && splitcomb, PJ_EINVAL); + PJ_ASSERT_RETURN(splitcomb, PJ_EINVAL); + PJ_UNUSED_ARG(pool); /* Make sure this is really a splitcomb port */ PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL); @@ -328,7 +354,7 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool, sc_afd = pjmedia_format_get_audio_format_detail(&splitcomb->info.fmt, 1); /* Create the port */ - rport = PJ_POOL_ZALLOC_T(pool, struct reverse_port); + rport = PJ_POOL_ZALLOC_T(sc->pool, struct reverse_port); rport->parent = sc; rport->ch_num = ch_num; @@ -392,6 +418,12 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool, sc->port_desc[ch_num].port = &rport->base; sc->port_desc[ch_num].reversed = PJ_TRUE; + /* Init group lock */ + status = pjmedia_port_init_grp_lock(port, sc->pool, sc->base.grp_lock); + if (status != PJ_SUCCESS) { + rport_on_destroy(&rport->base); + return status; + } /* Done */ *p_chport = port; @@ -688,10 +720,20 @@ static pj_status_t get_frame(pjmedia_port *this_port, static pj_status_t on_destroy(pjmedia_port *this_port) { - /* Nothing to do for the splitcomb - * Reverse ports must be destroyed separately. - */ - PJ_UNUSED_ARG(this_port); + struct splitcomb *sc = (struct splitcomb*) this_port; + unsigned ch; + + /* Decrement reference for non-reserved channels */ + for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) { + pjmedia_port *port = sc->port_desc[ch].port; + + if (!port || sc->port_desc[ch].reversed) + continue; + + pjmedia_port_dec_ref(port); + } + + pj_pool_release(sc->pool); return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index 4a9cb09e05..e4c5f7986b 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -990,7 +990,7 @@ static void create_dtmf_payload(pjmedia_stream *stream, event = (pjmedia_rtp_dtmf_event*) frame_out->buf; if (digit->duration == 0) { - PJ_LOG(5,(stream->port.info.name.ptr, "Sending DTMF digit id %c", + PJ_LOG(4,(stream->port.info.name.ptr, "Sending DTMF digit id %c", digitmap[digit->event])); *first = 1; } @@ -2455,7 +2455,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, PJ_ASSERT_RETURN(endpt && info && p_stream, PJ_EINVAL); - if (1 || pool == NULL) { + /* Must create own pool to avoid premature destroy */ + if (1 /* || pool == NULL */) { own_pool = pjmedia_endpt_create_pool( endpt, "strm%p", PJMEDIA_STREAM_SIZE, PJMEDIA_STREAM_INC); @@ -2492,8 +2493,6 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, 16, 80); afd = pjmedia_format_get_audio_format_detail(&stream->port.info.fmt, 1); - /* Init port. */ - //No longer there in 2.0 //pj_strdup(pool, &stream->port.info.encoding_name, &info->fmt.encoding_name); afd->clock_rate = info->fmt.clock_rate; @@ -2904,7 +2903,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, /* Attach handler to group lock from transport */ if (tp->grp_lock) { - stream->grp_lock = tp->grp_lock; + stream->grp_lock = stream->port.grp_lock = tp->grp_lock; pj_grp_lock_add_ref(stream->grp_lock); pj_grp_lock_add_handler(stream->grp_lock, pool, stream, &stream_on_destroy); @@ -3077,6 +3076,37 @@ static void stream_on_destroy(void *arg) { pjmedia_stream* stream = (pjmedia_stream*)arg; + /* This function may be called when stream is partly initialized. */ + if (stream->jb_mutex) + pj_mutex_lock(stream->jb_mutex); + + /* Free codec. */ + + if (stream->codec) { + pjmedia_codec_close(stream->codec); + pjmedia_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec); + stream->codec = NULL; + } + + /* Free mutex */ + + if (stream->jb_mutex) { + pj_mutex_unlock(stream->jb_mutex); + pj_mutex_destroy(stream->jb_mutex); + stream->jb_mutex = NULL; + } + + /* Destroy jitter buffer */ + if (stream->jb) + pjmedia_jbuf_destroy(stream->jb); + +#if TRACE_JB + if (TRACE_JB_OPENED(stream)) { + pj_file_close(stream->trace_jb_fd); + stream->trace_jb_fd = TRACE_JB_INVALID_FD; + } +#endif + PJ_LOG(4,(stream->port.info.name.ptr, "Stream destroyed")); pj_pool_safe_release(&stream->own_pool); } @@ -3156,41 +3186,9 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) */ if (stream->transport) { pjmedia_transport_detach(stream->transport, stream); - stream->transport = NULL; + //stream->transport = NULL; } - /* This function may be called when stream is partly initialized. */ - if (stream->jb_mutex) - pj_mutex_lock(stream->jb_mutex); - - - /* Free codec. */ - - if (stream->codec) { - pjmedia_codec_close(stream->codec); - pjmedia_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec); - stream->codec = NULL; - } - - /* Free mutex */ - - if (stream->jb_mutex) { - pj_mutex_unlock(stream->jb_mutex); - pj_mutex_destroy(stream->jb_mutex); - stream->jb_mutex = NULL; - } - - /* Destroy jitter buffer */ - if (stream->jb) - pjmedia_jbuf_destroy(stream->jb); - -#if TRACE_JB - if (TRACE_JB_OPENED(stream)) { - pj_file_close(stream->trace_jb_fd); - stream->trace_jb_fd = TRACE_JB_INVALID_FD; - } -#endif - if (stream->grp_lock) { pj_grp_lock_dec_ref(stream->grp_lock); } else { diff --git a/pjmedia/src/pjmedia/tonegen.c b/pjmedia/src/pjmedia/tonegen.c index ea667818fb..9cd26d390d 100644 --- a/pjmedia/src/pjmedia/tonegen.c +++ b/pjmedia/src/pjmedia/tonegen.c @@ -351,6 +351,7 @@ enum flags struct tonegen { pjmedia_port base; + pj_pool_t *pool; /* options */ unsigned options; @@ -409,7 +410,7 @@ static pj_status_t tonegen_destroy(pjmedia_port *this_port); * When the tone generator is first created, it will be loaded with the * default digit map. */ -PJ_DEF(pj_status_t) pjmedia_tonegen_create2(pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_tonegen_create2(pj_pool_t *pool_, const pj_str_t *name, unsigned clock_rate, unsigned channel_count, @@ -420,23 +421,31 @@ PJ_DEF(pj_status_t) pjmedia_tonegen_create2(pj_pool_t *pool, { const pj_str_t STR_TONE_GEN = pj_str("tonegen"); struct tonegen *tonegen; - pj_status_t status; + pj_status_t status = PJ_SUCCESS; + pj_pool_t *pool = NULL; - PJ_ASSERT_RETURN(pool && clock_rate && channel_count && + PJ_ASSERT_RETURN(pool_ && clock_rate && channel_count && samples_per_frame && bits_per_sample == 16 && p_port != NULL, PJ_EINVAL); /* Only support mono and stereo */ PJ_ASSERT_RETURN(channel_count==1 || channel_count==2, PJ_EINVAL); + if (name == NULL || name->slen == 0) + name = &STR_TONE_GEN; + + /* Create own pool */ + pool = pj_pool_create(pool_->factory, name->ptr, 500, 500, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + /* Create and initialize port */ tonegen = PJ_POOL_ZALLOC_T(pool, struct tonegen); - if (name == NULL || name->slen == 0) name = &STR_TONE_GEN; + tonegen->pool = pool; status = pjmedia_port_info_init(&tonegen->base.info, name, SIGNATURE, clock_rate, channel_count, bits_per_sample, samples_per_frame); if (status != PJ_SUCCESS) - return status; + goto on_return; tonegen->options = options; tonegen->base.get_frame = &tonegen_get_frame; @@ -454,7 +463,7 @@ PJ_DEF(pj_status_t) pjmedia_tonegen_create2(pj_pool_t *pool, } if (status != PJ_SUCCESS) { - return status; + goto on_return; } TRACE_((THIS_FILE, "Tonegen created: %u/%u/%u/%u", clock_rate, @@ -462,7 +471,13 @@ PJ_DEF(pj_status_t) pjmedia_tonegen_create2(pj_pool_t *pool, /* Done */ *p_port = &tonegen->base; - return PJ_SUCCESS; + +on_return: + if (status != PJ_SUCCESS) { + if (pool) + pj_pool_release(pool); + } + return status; } @@ -911,6 +926,9 @@ PJ_DEF(pj_status_t) pjmedia_tonegen_set_digit_map(pjmedia_port *port, pj_lock_release(tonegen->lock); + if (tonegen->pool) + pj_pool_safe_release(&tonegen->pool); + return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/vid_conf.c b/pjmedia/src/pjmedia/vid_conf.c index 7509f013c2..2620b21b0b 100644 --- a/pjmedia/src/pjmedia/vid_conf.c +++ b/pjmedia/src/pjmedia/vid_conf.c @@ -93,6 +93,7 @@ typedef struct vconf_port unsigned idx; /**< Port index. */ pj_str_t name; /**< Port name. */ pjmedia_port *port; /**< Video port. */ + pj_bool_t is_new; /**< Port newly added? */ pjmedia_format format; /**< Copy of port format info. */ pj_uint32_t ts_interval; /**< Port put/get interval. */ pj_timestamp ts_next; /**< Time for next put/get_frame(). */ @@ -340,6 +341,9 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_destroy(pjmedia_vid_conf *vid_conf) vid_conf->clock = NULL; } + /* Flush any pending operation (connect, disconnect, etc) */ + handle_op_queue(vid_conf); + /* Remove any registered ports (at least to cleanup their pool) */ for (i=0; i < vid_conf->opt.max_slot_cnt; ++i) { if (vid_conf->ports[i]) { @@ -395,19 +399,16 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_add_port( pjmedia_vid_conf *vid_conf, pj_mutex_lock(vid_conf->mutex); - if (vid_conf->port_cnt >= vid_conf->opt.max_slot_cnt) { - PJ_PERROR(3,(THIS_FILE, PJ_ETOOMANY, "Add port %s failed", name->ptr)); - pj_assert(!"Too many ports"); - pj_mutex_unlock(vid_conf->mutex); - return PJ_ETOOMANY; - } - /* Find empty port in the conference bridge. */ for (index=0; index < vid_conf->opt.max_slot_cnt; ++index) { if (vid_conf->ports[index] == NULL) break; } - pj_assert(index != vid_conf->opt.max_slot_cnt); + if (index == vid_conf->opt.max_slot_cnt) { + PJ_PERROR(3,(THIS_FILE, PJ_ETOOMANY, "Add port %s failed", name->ptr)); + pj_mutex_unlock(vid_conf->mutex); + return PJ_ETOOMANY; + } /* Create pool */ pool = pj_pool_create(parent_pool->factory, name->ptr, 500, 500, NULL); @@ -553,9 +554,14 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_add_port( pjmedia_vid_conf *vid_conf, goto on_error; } - /* Register the conf port. */ + /* Video data flow is not protected, avoid processing this newly + * added port. + */ + cport->is_new = PJ_TRUE; + + /* Register the conf port, but don't add port counter yet */ vid_conf->ports[index] = cport; - vid_conf->port_cnt++; + //vid_conf->port_cnt++; PJ_LOG(4,(THIS_FILE,"Added port %d (%.*s)", index, (int)cport->name.slen, cport->name.ptr)); @@ -1032,6 +1038,17 @@ static void on_clock_tick(const pj_timestamp *now, void *user_data) if (!pj_list_empty(vid_conf->op_queue)) { pj_mutex_lock(vid_conf->mutex); handle_op_queue(vid_conf); + + /* Activate any newly added port */ + for (i=0; iopt.max_slot_cnt; ++i) { + vconf_port *port = vid_conf->ports[i]; + if (!port || !port->is_new) + continue; + + port->is_new = PJ_FALSE; + ++vid_conf->port_cnt; + } + pj_mutex_unlock(vid_conf->mutex); } @@ -1054,8 +1071,8 @@ static void on_clock_tick(const pj_timestamp *now, void *user_data) vconf_port *sink = vid_conf->ports[i]; pjmedia_format *cur_fmt, *new_fmt; - /* Skip empty port */ - if (!sink) + /* Skip empty or new port */ + if (!sink || sink->is_new) continue; /* Increment occupied port counter */ diff --git a/pjmedia/src/pjmedia/wav_player.c b/pjmedia/src/pjmedia/wav_player.c index 410cf26272..774ddda180 100644 --- a/pjmedia/src/pjmedia/wav_player.c +++ b/pjmedia/src/pjmedia/wav_player.c @@ -55,6 +55,7 @@ struct file_reader_port { pjmedia_port base; + pj_pool_t *pool; unsigned options; pjmedia_wave_fmt_tag fmt_tag; pj_uint16_t bytes_per_sample; @@ -87,8 +88,10 @@ static struct file_reader_port *create_file_port(pj_pool_t *pool) struct file_reader_port *port; port = PJ_POOL_ZALLOC_T(pool, struct file_reader_port); - if (!port) + if (!port) { + pj_pool_release(pool); return NULL; + } /* Put in default values. * These will be overriden once the file is read. @@ -217,7 +220,7 @@ static pj_status_t read_wav_until(struct file_reader_port *fport, /* * Create WAVE player port. */ -PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool_, const char *filename, unsigned ptime, unsigned options, @@ -234,14 +237,16 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, unsigned samples_per_frame; pjmedia_wave_subchunk chunk; pj_status_t status = PJ_SUCCESS; + pj_pool_t *pool = NULL; /* Check arguments. */ - PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL); + PJ_ASSERT_RETURN(pool_ && filename && p_port, PJ_EINVAL); /* Check the file really exists. */ if (!pj_file_exists(filename)) { - return PJ_ENOTFOUND; + status = PJ_ENOTFOUND; + goto on_error; } /* Normalize ptime */ @@ -252,11 +257,20 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, if (buff_size < 1) buff_size = PJMEDIA_FILE_PORT_BUFSIZE; + /* Create own pool */ + pool = pj_pool_create(pool_->factory, filename, 500, 500, NULL); + if (!pool) { + status = PJ_ENOMEM; + goto on_error; + } + /* Create fport instance. */ fport = create_file_port(pool); if (!fport) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } + fport->pool = pool; /* Get the file size. */ @@ -264,25 +278,28 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, /* Size must be more than WAVE header size */ if (fport->fsize <= (pj_off_t)sizeof(pjmedia_wave_hdr)) { - return PJMEDIA_ENOTVALIDWAVE; + status = PJMEDIA_ENOTVALIDWAVE; + goto on_error; } /* Open file. */ status = pj_file_open(pool, filename, PJ_O_RDONLY | PJ_O_CLOEXEC, &fport->fd); if (status != PJ_SUCCESS) - return status; + goto on_error; + /* Read the RIFF file header only. */ size_to_read = size_read = sizeof(wave_hdr.riff_hdr); status = pj_file_read( fport->fd, &wave_hdr, &size_read); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } if (size_read != size_to_read) { pj_file_close(fport->fd); - return PJMEDIA_ENOTVALIDWAVE; + status = PJMEDIA_ENOTVALIDWAVE; + goto on_error; } /* Normalize WAVE header fields values from little-endian to host @@ -299,14 +316,15 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, "actual value|expected riff=%x|%x, wave=%x|%x", wave_hdr.riff_hdr.riff, PJMEDIA_RIFF_TAG, wave_hdr.riff_hdr.wave, PJMEDIA_WAVE_TAG)); - return PJMEDIA_ENOTVALIDWAVE; + status = PJMEDIA_ENOTVALIDWAVE; + goto on_error; } /* Read the WAVE file until we find 'fmt ' chunk. */ status = read_wav_until(fport, PJMEDIA_FMT_TAG, &chunk); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } pj_memcpy(&wave_hdr.fmt_hdr, &chunk, sizeof(chunk)); @@ -316,7 +334,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, status = pj_file_read(fport->fd, &wave_hdr.fmt_hdr.fmt_tag, &size_read); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } pjmedia_wave_hdr_file_to_host(&wave_hdr); @@ -343,7 +361,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } fport->fmt_tag = (pjmedia_wave_fmt_tag)wave_hdr.fmt_hdr.fmt_tag; @@ -360,7 +378,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, status = pj_file_setpos(fport->fd, size_to_read, PJ_SEEK_CUR); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } } @@ -368,7 +386,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, status = read_wav_until(fport, PJMEDIA_DATA_TAG, &chunk); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } PJMEDIA_WAVE_NORMALIZE_SUBCHUNK(&chunk); @@ -394,7 +412,8 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, wave_hdr.fmt_hdr.nchan / 1000) { pj_file_close(fport->fd); - return PJMEDIA_EWAVETOOSHORT; + status = PJMEDIA_EWAVETOOSHORT; + goto on_error; } /* It seems like we have a valid WAVE file. */ @@ -430,14 +449,16 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, */ if (samples_per_frame * fport->bytes_per_sample > fport->bufsize) { pj_file_close(fport->fd); - return PJ_EINVAL; + status = PJ_EINVAL; + goto on_error; } /* Create buffer. */ fport->buf = (char*) pj_pool_alloc(pool, fport->bufsize); if (!fport->buf) { pj_file_close(fport->fd); - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } fport->readpos = fport->buf; @@ -449,7 +470,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, status = fill_buffer(fport); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } /* Done. */ @@ -468,6 +489,15 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, (unsigned long)(fport->fsize / 1000))); return PJ_SUCCESS; + +on_error: + if (pool) + pj_pool_release(pool); + + PJ_PERROR(1,(THIS_FILE, status, + "Failed creating file player '%s'", filename)); + + return status; } @@ -857,6 +887,9 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) fport->subscribed = PJ_FALSE; } + if (fport->pool) + pj_pool_safe_release(&fport->pool); + return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/wav_playlist.c b/pjmedia/src/pjmedia/wav_playlist.c index edd75d5ef4..7ecbe00c46 100644 --- a/pjmedia/src/pjmedia/wav_playlist.c +++ b/pjmedia/src/pjmedia/wav_playlist.c @@ -57,6 +57,7 @@ struct playlist_port { pjmedia_port base; + pj_pool_t *pool; unsigned options; pj_bool_t eof; pj_uint32_t bufsize; @@ -292,7 +293,7 @@ static pj_status_t file_fill_buffer(struct playlist_port *fport) /* * Create wave list player. */ -PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool_, const pj_str_t *port_label, const pj_str_t file_list[], int file_count, @@ -304,15 +305,16 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool, struct playlist_port *fport; pjmedia_audio_format_detail *afd; pj_off_t pos; - pj_status_t status; + pj_status_t status = PJ_SUCCESS; int index; pj_bool_t has_wave_info = PJ_FALSE; pj_str_t tmp_port_label; char filename[PJ_MAXPATH]; /* filename for open operations. */ + pj_pool_t *pool = NULL; /* Check arguments. */ - PJ_ASSERT_RETURN(pool && file_list && file_count && p_port, PJ_EINVAL); + PJ_ASSERT_RETURN(pool_ && file_list && file_count && p_port, PJ_EINVAL); /* Normalize port_label */ if (port_label == NULL || port_label->slen == 0) { @@ -343,11 +345,17 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool, if (ptime == 0) ptime = 20; + /* Create own pool */ + pool = pj_pool_create(pool_->factory, port_label->ptr, 1024, 1024, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + /* Create fport instance. */ fport = create_file_list_port(pool, port_label); if (!fport) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } + fport->pool = pool; afd = pjmedia_format_get_audio_format_detail(&fport->base.info.fmt, 1); @@ -359,42 +367,48 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool, fport->fd_list = (pj_oshandle_t*) pj_pool_zalloc(pool, sizeof(pj_oshandle_t)*file_count); if (!fport->fd_list) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } /* Create file size list */ fport->fsize_list = (pj_off_t*) pj_pool_alloc(pool, sizeof(pj_off_t)*file_count); if (!fport->fsize_list) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } /* Create start of WAVE data list */ fport->start_data_list = (unsigned*) pj_pool_alloc(pool, sizeof(unsigned)*file_count); if (!fport->start_data_list) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } /* Create data len list */ fport->data_len_list = (unsigned*) pj_pool_alloc(pool, sizeof(unsigned)*file_count); if (!fport->data_len_list) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } /* Create data left list */ fport->data_left_list = (unsigned*) pj_pool_alloc(pool, sizeof(unsigned)*file_count); if (!fport->data_left_list) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } /* Create file position list */ fport->fpos_list = (pj_off_t*) pj_pool_alloc(pool, sizeof(pj_off_t)*file_count); if (!fport->fpos_list) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } /* Create file buffer once for this operation. @@ -406,7 +420,8 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool, /* Create buffer. */ fport->buf = (char*) pj_pool_alloc(pool, fport->bufsize); if (!fport->buf) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } /* Initialize port */ @@ -628,12 +643,21 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool, return PJ_SUCCESS; + on_error: + for (index=0; indexfd_list[index] != 0) pj_file_close(fport->fd_list[index]); } + if (pool) + pj_pool_release(pool); + + PJ_PERROR(1,(THIS_FILE, status, + "Failed creating WAV playlist '%s'", + (int)port_label->slen, port_label->ptr)); + return status; } @@ -775,6 +799,9 @@ static pj_status_t file_list_on_destroy(pjmedia_port *this_port) for (index=0; indexmax_file; index++) pj_file_close(fport->fd_list[index]); + if (fport->pool) + pj_pool_safe_release(&fport->pool); + return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/wav_writer.c b/pjmedia/src/pjmedia/wav_writer.c index dbe491c9ce..fefc2baf56 100644 --- a/pjmedia/src/pjmedia/wav_writer.c +++ b/pjmedia/src/pjmedia/wav_writer.c @@ -35,6 +35,7 @@ struct file_port { pjmedia_port base; + pj_pool_t *pool; pjmedia_wave_fmt_tag fmt_tag; pj_uint16_t bytes_per_sample; @@ -62,7 +63,7 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port); /* * Create file writer port. */ -PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool_, const char *filename, unsigned sampling_rate, unsigned channel_count, @@ -77,18 +78,27 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, pj_ssize_t size; pj_str_t name; pj_status_t status; + pj_pool_t *pool = NULL; /* Check arguments. */ - PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL); + PJ_ASSERT_RETURN(pool_ && filename && p_port, PJ_EINVAL); /* Only supports 16bits per sample for now. * See flush_buffer(). */ PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); + /* Create own pool */ + pool = pj_pool_create(pool_->factory, filename, 500, 500, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + /* Create file port instance. */ fport = PJ_POOL_ZALLOC_T(pool, struct file_port); - PJ_ASSERT_RETURN(fport != NULL, PJ_ENOMEM); + if (!fport) { + status = PJ_ENOMEM; + goto on_error; + } + fport->pool = pool; /* Initialize port info. */ pj_strdup2(pool, &name, filename); @@ -118,7 +128,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, status = pj_file_open(pool, filename, PJ_O_WRONLY | PJ_O_CLOEXEC, &fport->fd); if (status != PJ_SUCCESS) - return status; + goto on_error; /* Initialize WAVE header */ pj_bzero(&wave_hdr, sizeof(pjmedia_wave_hdr)); @@ -163,7 +173,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, status = pj_file_write(fport->fd, &wave_hdr, &size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } /* Write FACT chunk if it stores compressed data */ @@ -171,13 +181,13 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, status = pj_file_write(fport->fd, &fact_chunk, &size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } size = 4; status = pj_file_write(fport->fd, &tmp, &size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } /* Write DATA chunk header */ @@ -185,14 +195,14 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, status = pj_file_write(fport->fd, &wave_hdr.data_hdr, &size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } } else { size = sizeof(pjmedia_wave_hdr); status = pj_file_write(fport->fd, &wave_hdr, &size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } } @@ -208,7 +218,8 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, fport->buf = (char*) pj_pool_alloc(pool, fport->bufsize); if (fport->buf == NULL) { pj_file_close(fport->fd); - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } fport->writepos = fport->buf; @@ -224,6 +235,17 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, return PJ_SUCCESS; + + +on_error: + + if (pool) + pj_pool_release(pool); + + PJ_PERROR(1,(THIS_FILE, status, + "Failed creating WAV writer '%s'", filename)); + + return status; } @@ -461,7 +483,7 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) pj_ssize_t bytes; pj_uint32_t wave_file_len; pj_uint32_t wave_data_len; - pj_status_t status; + pj_status_t status = PJ_SUCCESS; pj_uint32_t data_len_pos = DATA_LEN_POS; if (fport->subscribed) { @@ -477,7 +499,7 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) status = pj_file_getpos(fport->fd, &file_size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_return; } /* Calculate wave fields */ @@ -493,7 +515,7 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) status = pj_file_setpos(fport->fd, FILE_LEN_POS, PJ_SEEK_SET); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_return; } /* Write file_len */ @@ -501,7 +523,7 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) status = pj_file_write(fport->fd, &wave_file_len, &bytes); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_return; } /* Write samples_len in FACT chunk */ @@ -518,7 +540,7 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) status = pj_file_setpos(fport->fd, SAMPLES_LEN_POS, PJ_SEEK_SET); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_return; } /* Write samples_len */ @@ -526,7 +548,7 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) status = pj_file_write(fport->fd, &wav_samples_len, &bytes); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_return; } } @@ -534,7 +556,7 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) status = pj_file_setpos(fport->fd, data_len_pos, PJ_SEEK_SET); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_return; } /* Write file_len */ @@ -542,15 +564,18 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) status = pj_file_write(fport->fd, &wave_data_len, &bytes); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_return; } /* Close file */ status = pj_file_close(fport->fd); if (status != PJ_SUCCESS) - return status; + goto on_return; - /* Done. */ - return PJ_SUCCESS; +on_return: + if (fport->pool) + pj_pool_safe_release(&fport->pool); + + return status; } diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c index 83c41cfe94..26764ab0d5 100644 --- a/pjsip-apps/src/pjsua/pjsua_app.c +++ b/pjsip-apps/src/pjsua/pjsua_app.c @@ -1389,6 +1389,14 @@ void legacy_on_stopped(pj_bool_t restart) (*app_cfg.on_stopped)(restart, 1, NULL); } + +static void app_cleanup(pjsip_endpoint *endpt) +{ + PJ_UNUSED_ARG(endpt); + pj_pool_safe_release(&app_config.pool); +} + + /***************************************************************************** * Public API */ @@ -1430,7 +1438,10 @@ static pj_status_t app_init(void) /* Create pool for application */ app_config.pool = pjsua_pool_create("pjsua-app", 1000, 1000); - tmp_pool = pjsua_pool_create("tmp-pjsua", 1000, 1000);; + tmp_pool = pjsua_pool_create("tmp-pjsua", 1000, 1000); + + /* Queue pool release at PJLIB exit */ + pjsip_endpt_atexit(pjsua_get_pjsip_endpt(), &app_cleanup); /* Init CLI & its FE settings */ if (!app_running) { @@ -2190,7 +2201,15 @@ static pj_status_t app_destroy(void) pjsip_tls_setting_wipe_keys(&app_config.udp_cfg.tls_setting); #endif - pj_pool_safe_release(&app_config.pool); + /* The pool release has been scheduled via pjsip_endpt_atexit(). + * + * We can only release the pool after audio & video conference destroy. + * Note that pjsua_conf_remove_port()/pjsua_vid_conf_remove_port() + * is asynchronous, so when sound device is not active, PJMEDIA ports + * have not been removed from the conference (and destroyed) yet + * until the audio & video conferences are destroyed (in pjsua_destroy()). + */ + //pj_pool_safe_release(&app_config.pool); status = pjsua_destroy(); diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index bca4c6a90b..756ab8135c 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -606,8 +606,15 @@ void pjsua_aud_stop_stream(pjsua_call_media *call_med) } if (call_med->strm.a.media_port) { - if (call_med->strm.a.destroy_port) + pjmedia_port *stream_port; + + /* Destroy custom stream port if any & configured to */ + pjmedia_stream_get_port(call_med->strm.a.stream, &stream_port); + if (call_med->strm.a.destroy_port && + call_med->strm.a.media_port != stream_port) + { pjmedia_port_destroy(call_med->strm.a.media_port); + } call_med->strm.a.media_port = NULL; } From 8914efc1e66a6b9c5e9b12c27c6700796962a65a Mon Sep 17 00:00:00 2001 From: Riza Sulistyo Date: Tue, 29 Oct 2024 15:10:40 +0700 Subject: [PATCH 084/491] Implement async callback for Android mediacodec (#4105) * Implement async callback for Android mediacodec * move atomic_queue to pjlib * Change the interface * Add checks and doc. * Modification based on comments --- pjlib/build/Makefile | 8 +- pjlib/include/pj/atomic_queue.h | 97 +++ pjlib/include/pj/types.h | 7 +- pjlib/include/pjlib.h | 1 + pjlib/src/pj/atomic_queue.cpp | 185 ++++++ pjmedia/src/pjmedia-audiodev/oboe_dev.cpp | 124 +--- .../src/pjmedia-codec/and_aud_mediacodec.cpp | 293 ++++++--- .../src/pjmedia-codec/and_vid_mediacodec.cpp | 567 +++++++++++------- 8 files changed, 839 insertions(+), 443 deletions(-) create mode 100644 pjlib/include/pj/atomic_queue.h create mode 100644 pjlib/src/pj/atomic_queue.cpp diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile index d13951b042..a273560e01 100644 --- a/pjlib/build/Makefile +++ b/pjlib/build/Makefile @@ -31,10 +31,10 @@ export _LDFLAGS := $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ # export PJLIB_SRCDIR = ../src/pj export PJLIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ - activesock.o array.o config.o ctype.o errno.o except.o fifobuf.o \ - guid.o hash.o ip_helper_generic.o list.o lock.o log.o os_time_common.o \ - os_info.o pool.o pool_buf.o pool_caching.o pool_dbg.o rand.o \ - rbtree.o sock_common.o sock_qos_common.o \ + activesock.o array.o atomic_queue.o config.o ctype.o errno.o except.o \ + fifobuf.o guid.o hash.o ip_helper_generic.o list.o lock.o log.o \ + os_time_common.o os_info.o pool.o pool_buf.o pool_caching.o pool_dbg.o \ + rand.o rbtree.o sock_common.o sock_qos_common.o \ ssl_sock_common.o ssl_sock_ossl.o ssl_sock_gtls.o ssl_sock_dump.o \ ssl_sock_darwin.o string.o timer.o types.o unittest.o export PJLIB_CFLAGS += $(_CFLAGS) diff --git a/pjlib/include/pj/atomic_queue.h b/pjlib/include/pj/atomic_queue.h new file mode 100644 index 0000000000..8b977fe464 --- /dev/null +++ b/pjlib/include/pj/atomic_queue.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2024 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_ATOMIC_QUEUE_H__ +#define __PJ_ATOMIC_QUEUE_H__ + +/** + * @file atomic_queue.h + * @brief Atomic Queue operations + * @{ + * + * Atomic queue for a single consumer and producer. + * This cyclic queue employs a ring buffer for storage, maintaining read/write + * pointers without locks. It’s designed for one producer and one consumer, + * as having multiple producers/consumers could lead to conflicts with the + * read/write pointers. + * The producer uses #pj_atomic_queue_put() to add an item to the back + * of the queue, while the consumer uses #pj_atomic_queue_get() + * to retrieve an item from the front. + */ + +#include + +PJ_BEGIN_DECL + +/** + * Create a new Atomic Queue for single consumer and single producer case. + * + * @param pool The pool to allocate the atomic queue structure. + * @param max_item_cnt The maximum number of items that can be stored. + * @param item_size The size of each item. + * @param name The name of the queue. + * @param atomic_queue Pointer to hold the newly created Atomic Queue. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pj_atomic_queue_create(pj_pool_t *pool, + unsigned max_item_cnt, + unsigned item_size, + const char *name, + pj_atomic_queue_t **atomic_queue); + +/** + * Destroy the Atomic Queue. + * + * @param atomic_queue The Atomic Queue to be destroyed. + * + * @return PJ_SUCCESS if success. + */ +PJ_DECL(pj_status_t) pj_atomic_queue_destroy(pj_atomic_queue_t *atomic_queue); + +/** + * Put an item to the back of the queue. If the queue is almost full + * (the write pointer is right before the read pointer) the producer will + * forcefully discard the oldest item in the head of the queue by incrementing + * the read pointer. + * + * @param atomic_queue The Atomic Queue. + * @param item The pointer to the data to store. + * + * @return PJ_SUCCESS if success. + */ +PJ_DECL(pj_status_t) pj_atomic_queue_put(pj_atomic_queue_t *atomic_queue, + void *item); + +/** + * Get an item from the head of the queue. + * + * @param atomic_queue The Atomic Queue. + * @param item The pointer to data to get the data. + * + * @return PJ_SUCCESS if success. + */ +PJ_DECL(pj_status_t) pj_atomic_queue_get(pj_atomic_queue_t *atomic_queue, + void *item); + +/** + * @} + */ + +PJ_END_DECL + +#endif diff --git a/pjlib/include/pj/types.h b/pjlib/include/pj/types.h index cdee0797be..ccaaf7f4d1 100644 --- a/pjlib/include/pj/types.h +++ b/pjlib/include/pj/types.h @@ -242,7 +242,12 @@ typedef struct pj_atomic_t pj_atomic_t; * Value type of an atomic variable. */ typedef PJ_ATOMIC_VALUE_TYPE pj_atomic_value_t; - + +/** + * Opaque data type for atomic queue. + */ +typedef struct pj_atomic_queue_t pj_atomic_queue_t; + /* ************************************************************************* */ /** Thread handle. */ diff --git a/pjlib/include/pjlib.h b/pjlib/include/pjlib.h index eb9e757842..2ccbff7a1e 100644 --- a/pjlib/include/pjlib.h +++ b/pjlib/include/pjlib.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/pjlib/src/pj/atomic_queue.cpp b/pjlib/src/pj/atomic_queue.cpp new file mode 100644 index 0000000000..2b839af217 --- /dev/null +++ b/pjlib/src/pj/atomic_queue.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2024 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include + +#if 0 +# define TRACE_(arg) PJ_LOG(4,arg) +#else +# define TRACE_(expr) +#endif +class AtomicQueue { +public: + /** + * Constructor + */ + AtomicQueue(unsigned maxItemCnt, unsigned itemSize, const char* name = ""): + maxItemCnt_(maxItemCnt), + itemSize_(itemSize), + ptrWrite(0), + ptrRead(0), + buffer(NULL), + name_(name) + { + buffer = new char[maxItemCnt_ * itemSize_]; + + /* Surpress warning when debugging log is disabled */ + PJ_UNUSED_ARG(name_); + + TRACE_((name_, "Created AtomicQueue: maxItemCnt=%d itemSize=%d", + maxItemCnt_, itemSize_)); + } + + /** + * Destructor + */ + ~AtomicQueue() + { + delete [] buffer; + } + + /** + * Get a item from the head of the queue + */ + bool get(void* item) + { + if (ptrRead == ptrWrite) + return false; + + unsigned cur_ptr = ptrRead; + void *p = &buffer[cur_ptr * itemSize_]; + pj_memcpy(item, p, itemSize_); + inc_ptrRead_if_not_yet(cur_ptr); + + TRACE_((name_, "GET: ptrRead=%d ptrWrite=%d\n", + ptrRead.load(), ptrWrite.load())); + + return true; + } + + /** + * Put a item to the back of the queue + */ + void put(void* item) + { + unsigned cur_ptr = ptrWrite; + void *p = &buffer[cur_ptr * itemSize_]; + pj_memcpy(p, item, itemSize_); + unsigned next_ptr = inc_ptrWrite(cur_ptr); + + /* Increment read pointer if next write is overlapping + * (next_ptr == read ptr) + */ + unsigned next_read_ptr = (next_ptr == maxItemCnt_-1)? 0 : (next_ptr+1); + ptrRead.compare_exchange_strong(next_ptr, next_read_ptr); + + TRACE_((name_, "PUT: ptrRead=%d ptrWrite=%d\n", + ptrRead.load(), ptrWrite.load())); + } + +private: + unsigned maxItemCnt_; + unsigned itemSize_; + std::atomic ptrWrite; + std::atomic ptrRead; + char *buffer; + const char *name_; + + /* Increment read pointer, only if producer not incemented it already. + * Producer may increment the read pointer if the write pointer is + * right before the read pointer (buffer almost full). + */ + bool inc_ptrRead_if_not_yet(unsigned old_ptr) + { + unsigned new_ptr = (old_ptr == maxItemCnt_-1)? 0 : (old_ptr+1); + return ptrRead.compare_exchange_strong(old_ptr, new_ptr); + } + + /* Increment write pointer */ + unsigned inc_ptrWrite(unsigned old_ptr) + { + unsigned new_ptr = (old_ptr == maxItemCnt_-1)? 0 : (old_ptr+1); + if (ptrWrite.compare_exchange_strong(old_ptr, new_ptr)) + return new_ptr; + + /* Should never happen */ + pj_assert(!"There is more than one producer!"); + return old_ptr; + } + + AtomicQueue() {} +}; + +struct pj_atomic_queue_t +{ + AtomicQueue *aQ; +}; + +PJ_DEF(pj_status_t) pj_atomic_queue_create(pj_pool_t *pool, + unsigned max_item_cnt, + unsigned item_size, + const char *name, + pj_atomic_queue_t **atomic_queue) +{ + pj_atomic_queue_t *aqueue; + + PJ_ASSERT_RETURN(pool, PJ_EINVAL); + aqueue = PJ_POOL_ZALLOC_T(pool, pj_atomic_queue_t); + aqueue->aQ = new AtomicQueue(max_item_cnt, item_size, name); + *atomic_queue = aqueue; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_atomic_queue_destroy(pj_atomic_queue_t *atomic_queue) +{ + PJ_ASSERT_RETURN(atomic_queue && atomic_queue->aQ, PJ_EINVAL); + delete atomic_queue->aQ; + atomic_queue->aQ = NULL; + return PJ_SUCCESS; +} + +/** + * For producer, there is write pointer 'ptrWrite' that will be incremented + * every time a item is queued to the back of the queue. + */ +PJ_DEF(pj_status_t) pj_atomic_queue_put(pj_atomic_queue_t *atomic_queue, + void *item) +{ + PJ_ASSERT_RETURN(atomic_queue && atomic_queue->aQ && item, PJ_EINVAL); + atomic_queue->aQ->put(item); + return PJ_SUCCESS; +} + +/** + * For consumer, there is read pointer 'ptrRead' that will be incremented + * every time a item is fetched from the head of the queue, only if the + * pointer is not modified by producer (in case of queue full). + */ +PJ_DEF(pj_status_t) pj_atomic_queue_get(pj_atomic_queue_t *atomic_queue, + void *item) +{ + PJ_ASSERT_RETURN(atomic_queue && atomic_queue->aQ && item, PJ_EINVAL); + if (atomic_queue->aQ->get(item)) + return PJ_SUCCESS; + else + return PJ_ENOTFOUND; +} diff --git a/pjmedia/src/pjmedia-audiodev/oboe_dev.cpp b/pjmedia/src/pjmedia-audiodev/oboe_dev.cpp index a2981d2f3b..c0d8054202 100644 --- a/pjmedia/src/pjmedia-audiodev/oboe_dev.cpp +++ b/pjmedia/src/pjmedia-audiodev/oboe_dev.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -38,9 +39,6 @@ #include #include -#include - - /* Device info */ typedef struct aud_dev_info { @@ -525,106 +523,6 @@ static pj_status_t oboe_default_param(pjmedia_aud_dev_factory *ff, return PJ_SUCCESS; } - -/* Atomic queue (ring buffer) for single consumer & single producer. - * - * Producer invokes 'put(frame)' to put a frame to the back of the queue and - * consumer invokes 'get(frame)' to get a frame from the head of the queue. - * - * For producer, there is write pointer 'ptrWrite' that will be incremented - * every time a frame is queued to the back of the queue. If the queue is - * almost full (the write pointer is right before the read pointer) the - * producer will forcefully discard the oldest frame in the head of the - * queue by incrementing read pointer. - * - * For consumer, there is read pointer 'ptrRead' that will be incremented - * every time a frame is fetched from the head of the queue, only if the - * pointer is not modified by producer (in case of queue full). - */ -class AtomicQueue { -public: - - AtomicQueue(unsigned max_frame_cnt, unsigned frame_size, - const char* name_= "") : - maxFrameCnt(max_frame_cnt), frameSize(frame_size), - ptrWrite(0), ptrRead(0), - buffer(NULL), name(name_) - { - buffer = new char[maxFrameCnt * frameSize]; - - /* Surpress warning when debugging log is disabled */ - PJ_UNUSED_ARG(name); - } - - ~AtomicQueue() { - delete [] buffer; - } - - /* Get a frame from the head of the queue */ - bool get(void* frame) { - if (ptrRead == ptrWrite) - return false; - - unsigned cur_ptr = ptrRead; - void *p = &buffer[cur_ptr * frameSize]; - pj_memcpy(frame, p, frameSize); - inc_ptr_read_if_not_yet(cur_ptr); - - //__android_log_print(ANDROID_LOG_INFO, name, - // "GET: ptrRead=%d ptrWrite=%d\n", - // ptrRead.load(), ptrWrite.load()); - return true; - } - - /* Put a frame to the back of the queue */ - void put(void* frame) { - unsigned cur_ptr = ptrWrite; - void *p = &buffer[cur_ptr * frameSize]; - pj_memcpy(p, frame, frameSize); - unsigned next_ptr = inc_ptr_write(cur_ptr); - - /* Increment read pointer if next write is overlapping (next_ptr == read ptr) */ - unsigned next_read_ptr = (next_ptr == maxFrameCnt-1)? 0 : (next_ptr+1); - ptrRead.compare_exchange_strong(next_ptr, next_read_ptr); - - //__android_log_print(ANDROID_LOG_INFO, name, - // "PUT: ptrRead=%d ptrWrite=%d\n", - // ptrRead.load(), ptrWrite.load()); - } - -private: - - unsigned maxFrameCnt; - unsigned frameSize; - std::atomic ptrWrite; - std::atomic ptrRead; - char *buffer; - const char *name; - - /* Increment read pointer, only if producer not incemented it already. - * Producer may increment the read pointer if the write pointer is - * right before the read pointer (buffer almost full). - */ - bool inc_ptr_read_if_not_yet(unsigned old_ptr) { - unsigned new_ptr = (old_ptr == maxFrameCnt-1)? 0 : (old_ptr+1); - return ptrRead.compare_exchange_strong(old_ptr, new_ptr); - } - - /* Increment write pointer */ - unsigned inc_ptr_write(unsigned old_ptr) { - unsigned new_ptr = (old_ptr == maxFrameCnt-1)? 0 : (old_ptr+1); - if (ptrWrite.compare_exchange_strong(old_ptr, new_ptr)) - return new_ptr; - - /* Should never happen */ - pj_assert(!"There is more than one producer!"); - return old_ptr; - } - - AtomicQueue() {} -}; - - /* Interface to Oboe */ class MyOboeEngine : oboe::AudioStreamDataCallback, oboe::AudioStreamErrorCallback @@ -720,8 +618,9 @@ class MyOboeEngine : oboe::AudioStreamDataCallback, "Oboe stream %s queue size=%d frames (latency=%d ms)", dir_st, queue_size, latency)); - queue = new AtomicQueue(queue_size, stream->param.samples_per_frame*2, - dir_st); + pj_atomic_queue_create(stream->pool, queue_size, + stream->param.samples_per_frame*2, dir_st, + &queue); /* Create semaphore */ if (sem_init(&sem, 0, 0) != 0) { @@ -816,7 +715,7 @@ class MyOboeEngine : oboe::AudioStreamDataCallback, if (queue) { PJ_LOG(5,(THIS_FILE, "Oboe %s deleting queue", dir_st)); - delete queue; + pj_atomic_queue_destroy(queue); queue = NULL; } @@ -836,10 +735,10 @@ class MyOboeEngine : oboe::AudioStreamDataCallback, { if (dir == PJMEDIA_DIR_CAPTURE) { /* Put the audio frame to queue */ - queue->put(audioData); + pj_atomic_queue_put(this->queue, audioData); } else { /* Get audio frame from queue */ - if (!queue->get(audioData)) { + if (pj_atomic_queue_get(this->queue, audioData) != PJ_SUCCESS) { pj_bzero(audioData, stream->param.samples_per_frame*2); __android_log_write(ANDROID_LOG_WARN, THIS_FILE, "Oboe playback got an empty queue"); @@ -950,7 +849,7 @@ class MyOboeEngine : oboe::AudioStreamDataCallback, /* Queue a silent frame to playback buffer */ if (this_->dir == PJMEDIA_DIR_PLAYBACK) { - this_->queue->put(tmp_buf); + pj_atomic_queue_put(this_->queue, tmp_buf); } while (1) { @@ -963,7 +862,8 @@ class MyOboeEngine : oboe::AudioStreamDataCallback, bool stop_stream = false; /* Read audio frames from Oboe */ - while (this_->queue->get(tmp_buf)) { + while (pj_atomic_queue_get(this_->queue, tmp_buf) == PJ_SUCCESS) + { /* Send audio frame to app via callback rec_cb() */ pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; @@ -1006,7 +906,7 @@ class MyOboeEngine : oboe::AudioStreamDataCallback, /* Send audio frame to Oboe */ if (status == PJ_SUCCESS) { - this_->queue->put(tmp_buf); + pj_atomic_queue_put(this_->queue, tmp_buf); } else { /* App wants to stop audio dev stream */ break; @@ -1031,7 +931,7 @@ class MyOboeEngine : oboe::AudioStreamDataCallback, pj_thread_t *thread; volatile pj_bool_t thread_quit; sem_t sem; - AtomicQueue *queue; + pj_atomic_queue_t *queue; pj_timestamp ts; bool err_thread_registered; pj_thread_desc err_thread_desc; diff --git a/pjmedia/src/pjmedia-codec/and_aud_mediacodec.cpp b/pjmedia/src/pjmedia-codec/and_aud_mediacodec.cpp index 5b95181d78..1f533457ca 100644 --- a/pjmedia/src/pjmedia-codec/and_aud_mediacodec.cpp +++ b/pjmedia/src/pjmedia-codec/and_aud_mediacodec.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #if defined(PJMEDIA_HAS_ANDROID_MEDIACODEC) && \ PJMEDIA_HAS_ANDROID_MEDIACODEC != 0 +#include /* Android AMediaCodec: */ #include "media/NdkMediaCodec.h" @@ -47,10 +49,13 @@ #define AND_MEDIA_KEY_BITRATE "bitrate" #define AND_MEDIA_KEY_MIME "mime" -#define CODEC_WAIT_RETRY 10 -#define CODEC_THREAD_WAIT 10 -/* Timeout until the buffer is ready in ms. */ -#define CODEC_DEQUEUE_TIMEOUT 10 +#define BUFFER_MAX_ITEM 16 + +typedef struct and_med_buf_info { + pj_int32_t index; + pj_int32_t size; + pj_uint32_t flags; +} and_med_buf_info; /* Prototypes for Android MediaCodec codecs factory */ static pj_status_t and_media_test_alloc(pjmedia_codec_factory *factory, @@ -151,6 +156,15 @@ typedef struct and_media_private { pjmedia_silence_det *vad; /**< PJMEDIA VAD engine, NULL if codec has internal VAD. */ pj_timestamp last_tx; /**< Timestamp of last transmit.*/ + + pj_atomic_queue_t *enc_avail_input_buf; /**< Encoder available input + buffer */ + pj_atomic_queue_t *enc_avail_output_buf; /**< Encoder available ouput + buffer */ + pj_atomic_queue_t *dec_avail_input_buf; /**< Decoder available input + buffer */ + pj_atomic_queue_t *dec_avail_output_buf; /**< Decoder available output + buffer */ } and_media_private_t; /* CUSTOM CALLBACKS */ @@ -210,6 +224,81 @@ static pj_str_t AMRWB_decoder[] = {{(char *)"OMX.google.amrwb.decoder\0", 24}, {(char *)"c2.android.amrwb.decoder\0", 24}}; #endif +/** + * Called when an input buffer becomes available. + * The specified index is the index of the available input buffer. + */ +static void and_med_on_input_avail(AMediaCodec *codec, + void *userdata, + int32_t index) +{ + and_media_private_t *and_media_data = (and_media_private_t *) userdata; + and_med_buf_info buf_info; + pj_atomic_queue_t *buf_queue; + pj_bzero(&buf_info, sizeof(buf_info)); + + if (codec == and_media_data->enc) { + buf_queue = and_media_data->enc_avail_input_buf; + } else { + buf_queue = and_media_data->dec_avail_input_buf; + } + buf_info.index = index; + pj_atomic_queue_put(buf_queue, &buf_info); +} +/** + * Called when an output buffer becomes available. + * The specified index is the index of the available output buffer. + */ +static void and_med_on_output_avail(AMediaCodec *codec, + void *userdata, + int32_t index, + AMediaCodecBufferInfo *bufferInfo) +{ + and_media_private_t *and_media_data = (and_media_private_t *) userdata; + and_med_buf_info buf_info; + pj_atomic_queue_t *buf_queue; + pj_bzero(&buf_info, sizeof(buf_info)); + if (codec == and_media_data->enc) { + buf_queue = and_media_data->enc_avail_output_buf; + } else { + buf_queue = and_media_data->dec_avail_output_buf; + } + buf_info.index = index; + buf_info.size = bufferInfo->size; + buf_info.flags = bufferInfo->flags; + pj_atomic_queue_put(buf_queue, &buf_info); +} + +/** + * Called when the output format has changed. + * The specified format contains the new output format. + */ +static void and_med_on_format_changed(AMediaCodec *codec, + void *userdata, + AMediaFormat *format) +{ + and_media_private_t *and_media_data = (and_media_private_t *) userdata; + __android_log_print(ANDROID_LOG_INFO, THIS_FILE, + "[%s] On format changed\r\n", + (codec==and_media_data->enc)?"encoder":"decoder"); +} + +/** + * Called when the MediaCodec encountered an error. + */ +static void and_med_on_error(AMediaCodec *codec, + void *userdata, + media_status_t error, + int32_t actionCode, + const char *detail) + { + and_media_private_t *and_media_data = (and_media_private_t *) userdata; + __android_log_print(ANDROID_LOG_INFO, THIS_FILE, + "[%s] On Media error : err[%d] code[%d] msg[%s]\r\n", + (codec==and_media_data->enc)?"encoder":"decoder", error, + actionCode, detail); + } + /* Android MediaCodec codec implementation descriptions. */ static struct and_media_codec { int enabled; /* Is this codec enabled? */ @@ -707,6 +796,17 @@ static void create_codec(and_media_private_t *and_media_data) if (!and_media_data->enc) { PJ_LOG(4, (THIS_FILE, "Failed creating encoder: %s", enc_name)); } + pj_atomic_queue_create(and_media_data->pool, + BUFFER_MAX_ITEM, + sizeof(and_med_buf_info), + "enc_input_buf", + &and_media_data->enc_avail_input_buf); + pj_atomic_queue_create(and_media_data->pool, + BUFFER_MAX_ITEM, + sizeof(and_med_buf_info), + "enc_output_buf", + &and_media_data->enc_avail_output_buf); + PJ_LOG(4, (THIS_FILE, "Done creating encoder: %s [0x%p]", enc_name, and_media_data->enc)); } @@ -716,6 +816,17 @@ static void create_codec(and_media_private_t *and_media_data) if (!and_media_data->dec) { PJ_LOG(4, (THIS_FILE, "Failed creating decoder: %s", dec_name)); } + pj_atomic_queue_create(and_media_data->pool, + BUFFER_MAX_ITEM, + sizeof(and_med_buf_info), + "dec_input_buf", + &and_media_data->dec_avail_input_buf); + pj_atomic_queue_create(and_media_data->pool, + BUFFER_MAX_ITEM, + sizeof(and_med_buf_info), + "dec_output_buf", + &and_media_data->dec_avail_output_buf); + PJ_LOG(4, (THIS_FILE, "Done creating decoder: %s [0x%p]", dec_name, and_media_data->dec)); } @@ -826,12 +937,20 @@ static pj_status_t and_media_dealloc_codec(pjmedia_codec_factory *factory, AMediaCodec_stop(codec_data->enc); AMediaCodec_delete(codec_data->enc); codec_data->enc = NULL; + pj_atomic_queue_destroy(codec_data->enc_avail_input_buf); + codec_data->enc_avail_input_buf = NULL; + pj_atomic_queue_destroy(codec_data->enc_avail_output_buf); + codec_data->enc_avail_output_buf = NULL; } if (codec_data->dec) { AMediaCodec_stop(codec_data->dec); AMediaCodec_delete(codec_data->dec); codec_data->dec = NULL; + pj_atomic_queue_destroy(codec_data->dec_avail_input_buf); + codec_data->dec_avail_input_buf = NULL; + pj_atomic_queue_destroy(codec_data->dec_avail_output_buf); + codec_data->dec_avail_output_buf = NULL; } pj_pool_release(codec_data->pool); @@ -868,6 +987,10 @@ static pj_status_t and_media_codec_open(pjmedia_codec *codec, codec_data->vad_enabled = (attr->setting.vad != 0); codec_data->plc_enabled = (attr->setting.plc != 0); and_media_data->clock_rate = attr->info.clock_rate; + AMediaCodecOnAsyncNotifyCallback async_cb = {&and_med_on_input_avail, + &and_med_on_output_avail, + &and_med_on_format_changed, + &and_med_on_error}; #if PJMEDIA_HAS_AND_MEDIA_AMRNB if (and_media_data->codec_id == AND_AUD_CODEC_AMRNB || @@ -937,8 +1060,9 @@ static pj_status_t and_media_codec_open(pjmedia_codec *codec, } ++p; } - if (diff == 99) + if (diff == 99) { goto on_error; + } enc_mode = (pj_int8_t)(enc_mode + diff); @@ -969,6 +1093,9 @@ static pj_status_t and_media_codec_open(pjmedia_codec *codec, s->dec_setting.octet_aligned, s->dec_setting.reorder)); } #endif + AMediaCodec_setAsyncNotifyCallback(codec_data->enc, async_cb, codec_data); + AMediaCodec_setAsyncNotifyCallback(codec_data->dec, async_cb, codec_data); + status = configure_codec(codec_data, PJ_TRUE); if (status != PJ_SUCCESS) { goto on_error; @@ -1107,82 +1234,71 @@ static pj_status_t and_media_codec_encode(pjmedia_codec *codec, /* Encode the frames */ while (nsamples >= samples_per_frame) { - pj_ssize_t buf_idx; - unsigned i; pj_size_t output_size; pj_uint8_t *output_buf; - AMediaCodecBufferInfo buf_info; - - buf_idx = AMediaCodec_dequeueInputBuffer(codec_data->enc, - CODEC_DEQUEUE_TIMEOUT); - - if (buf_idx >= 0) { - media_status_t am_status; - pj_size_t output_size; - unsigned input_size = samples_per_frame << 1; - - pj_uint8_t *input_buf = AMediaCodec_getInputBuffer(codec_data->enc, - buf_idx, &output_size); - - if (input_buf && output_size >= input_size) { - pj_memcpy(input_buf, pcm_in, input_size); - - am_status = AMediaCodec_queueInputBuffer(codec_data->enc, - buf_idx, 0, input_size, 0, 0); - if (am_status != AMEDIA_OK) { - PJ_LOG(4, (THIS_FILE, "Encoder queueInputBuffer return %d", - am_status)); - goto on_return; - } - } else { - if (!input_buf) { - PJ_LOG(4,(THIS_FILE, "Encoder getInputBuffer " - "returns no input buff")); - } else { - PJ_LOG(4,(THIS_FILE, "Encoder getInputBuffer " - "size: %lu, expecting %d.", - (unsigned long)output_size, - input_size)); - } + media_status_t am_status; + unsigned input_size; + pj_uint8_t *input_buf; + and_med_buf_info buf_info; + pj_atomic_queue_t *queue = codec_data->enc_avail_input_buf; + + pj_bzero(&buf_info, sizeof(buf_info)); + if (pj_atomic_queue_get(queue, &buf_info) != PJ_SUCCESS || + buf_info.index < 0) + { + PJ_LOG(4,(THIS_FILE, "Encoder failed to get input Buffer[%d]", + buf_info.index)); + goto on_return; + } + input_size = samples_per_frame << 1; + input_buf = AMediaCodec_getInputBuffer(codec_data->enc, + buf_info.index, &output_size); + if (input_buf && output_size >= input_size) { + pj_memcpy(input_buf, pcm_in, input_size); + + am_status = AMediaCodec_queueInputBuffer(codec_data->enc, + buf_info.index, 0, input_size, 0, 0); + if (am_status != AMEDIA_OK) { + PJ_LOG(4, (THIS_FILE, "Encoder queueInputBuffer return %d", + am_status)); goto on_return; } } else { - PJ_LOG(4,(THIS_FILE, "Encoder dequeueInputBuffer failed[%ld]", - buf_idx)); - goto on_return; - } - - for (i = 0; i < CODEC_WAIT_RETRY; ++i) { - buf_idx = AMediaCodec_dequeueOutputBuffer(codec_data->enc, - &buf_info, - CODEC_DEQUEUE_TIMEOUT); - if (buf_idx == -1) { - /* Timeout, wait until output buffer is availble. */ - pj_thread_sleep(CODEC_THREAD_WAIT); + if (!input_buf) { + PJ_LOG(4,(THIS_FILE, "Encoder getInputBuffer " + "returns no input buff")); } else { - break; + PJ_LOG(4,(THIS_FILE, "Encoder getInputBuffer " + "size: %lu, expecting %d.", + (unsigned long)output_size, + input_size)); } + goto on_return; } - if (buf_idx < 0) { - PJ_LOG(4, (THIS_FILE, "Encoder dequeueOutputBuffer failed %ld", - buf_idx)); + pj_bzero(&buf_info, sizeof(buf_info)); + queue = codec_data->enc_avail_output_buf; + if (pj_atomic_queue_get(queue, &buf_info) != PJ_SUCCESS || + buf_info.index < 0) + { + PJ_LOG(4, (THIS_FILE, "Encoder failed to get output Buffer[%d]", + buf_info.index)); goto on_return; } output_buf = AMediaCodec_getOutputBuffer(codec_data->enc, - buf_idx, + buf_info.index, &output_size); if (!output_buf) { PJ_LOG(4, (THIS_FILE, "Encoder failed getting output buffer, " - "buffer size=%d, flags %d", - buf_info.size, buf_info.flags)); + "index=%d buffer size=%d, flags %d", + buf_info.index, buf_info.size, buf_info.flags)); goto on_return; } pj_memcpy(bits_out, output_buf, buf_info.size); AMediaCodec_releaseOutputBuffer(codec_data->enc, - buf_idx, + buf_info.index, 0); bits_out += buf_info.size; tx += buf_info.size; @@ -1227,18 +1343,17 @@ static pj_status_t and_media_codec_decode(pjmedia_codec *codec, struct and_media_codec *and_media_data = &and_media_codec[codec_data->codec_idx]; unsigned samples_per_frame; - unsigned i; - - pj_ssize_t buf_idx = -1; + and_med_buf_info buf_info; pj_uint8_t *input_buf; pj_size_t input_size; pj_size_t output_size; media_status_t am_status; - AMediaCodecBufferInfo buf_info; pj_uint8_t *output_buf; pjmedia_frame input_; + pj_atomic_queue_t *queue; pj_bzero(&input_, sizeof(pjmedia_frame)); + pj_bzero(&buf_info, sizeof(buf_info)); samples_per_frame = and_media_data->samples_per_frame; PJ_ASSERT_RETURN(output_buf_len >= samples_per_frame << 1, @@ -1248,23 +1363,22 @@ static pj_status_t and_media_codec_decode(pjmedia_codec *codec, { goto on_return; } - - buf_idx = AMediaCodec_dequeueInputBuffer(codec_data->dec, - CODEC_DEQUEUE_TIMEOUT); - - if (buf_idx < 0) { - PJ_LOG(4,(THIS_FILE, "Decoder dequeueInputBuffer failed return %ld", - buf_idx)); + queue = codec_data->dec_avail_input_buf; + if (pj_atomic_queue_get(queue, &buf_info) != PJ_SUCCESS || + buf_info.index < 0) + { + PJ_LOG(4,(THIS_FILE, "Decoder failed to get input Buffer[%d]", + buf_info.index)); goto on_return; } input_buf = AMediaCodec_getInputBuffer(codec_data->dec, - buf_idx, + buf_info.index, &input_size); if (input_buf == 0) { PJ_LOG(4,(THIS_FILE, "Decoder getInputBuffer failed " - "return input_buf=%d, size=%lu", *input_buf, - (unsigned long)input_size)); + "return input_buf=%d, index=%d size=%lu", *input_buf, + buf_info.index, (unsigned long)input_size)); goto on_return; } @@ -1277,7 +1391,7 @@ static pj_status_t and_media_codec_decode(pjmedia_codec *codec, } am_status = AMediaCodec_queueInputBuffer(codec_data->dec, - buf_idx, + buf_info.index, 0, input_.size, input->timestamp.u32.lo, @@ -1288,31 +1402,22 @@ static pj_status_t and_media_codec_decode(pjmedia_codec *codec, goto on_return; } - for (i = 0; i < CODEC_WAIT_RETRY; ++i) { - buf_idx = AMediaCodec_dequeueOutputBuffer(codec_data->dec, - &buf_info, - CODEC_DEQUEUE_TIMEOUT); - if (buf_idx == -1) { - /* Timeout, wait until output buffer is availble. */ - PJ_LOG(5, (THIS_FILE, "Decoder dequeueOutputBuffer timeout[%d]", - i+1)); - pj_thread_sleep(CODEC_THREAD_WAIT); - } else { - break; - } - } - if (buf_idx < 0) { - PJ_LOG(5, (THIS_FILE, "Decoder dequeueOutputBuffer failed [%ld]", - buf_idx)); + pj_bzero(&buf_info, sizeof(buf_info)); + queue = codec_data->dec_avail_output_buf; + if (pj_atomic_queue_get(queue, &buf_info) != PJ_SUCCESS || + buf_info.index < 0) + { + PJ_LOG(4, (THIS_FILE, "Decoder failed to get output Buffer[%d]", + buf_info.index)); goto on_return; } output_buf = AMediaCodec_getOutputBuffer(codec_data->dec, - buf_idx, + buf_info.index, &output_size); if (output_buf == NULL) { am_status = AMediaCodec_releaseOutputBuffer(codec_data->dec, - buf_idx, + buf_info.index, 0); if (am_status != AMEDIA_OK) { PJ_LOG(4,(THIS_FILE, "Decoder releaseOutputBuffer failed %d", @@ -1326,7 +1431,7 @@ static pj_status_t and_media_codec_decode(pjmedia_codec *codec, output->size = buf_info.size; output->timestamp.u64 = input->timestamp.u64; am_status = AMediaCodec_releaseOutputBuffer(codec_data->dec, - buf_idx, + buf_info.index, 0); /* Invoke external PLC if codec has no internal PLC */ diff --git a/pjmedia/src/pjmedia-codec/and_vid_mediacodec.cpp b/pjmedia/src/pjmedia-codec/and_vid_mediacodec.cpp index cb898f3108..e5c0e7501a 100644 --- a/pjmedia/src/pjmedia-codec/and_vid_mediacodec.cpp +++ b/pjmedia/src/pjmedia-codec/and_vid_mediacodec.cpp @@ -21,12 +21,15 @@ #include #include #include +#include #include #if defined(PJMEDIA_HAS_ANDROID_MEDIACODEC) && \ PJMEDIA_HAS_ANDROID_MEDIACODEC != 0 && \ defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) +#include + /* Android AMediaCodec: */ #include "media/NdkMediaCodec.h" @@ -40,7 +43,7 @@ #define AND_MEDIA_KEY_BIT_RATE "bitrate" #define AND_MEDIA_KEY_PROFILE "profile" #define AND_MEDIA_KEY_FRAME_RATE "frame-rate" -#define AND_MEDIA_KEY_IFR_INTTERVAL "i-frame-interval" +#define AND_MEDIA_KEY_IFR_INTERVAL "i-frame-interval" #define AND_MEDIA_KEY_MIME "mime" #define AND_MEDIA_KEY_REQUEST_SYNCF "request-sync" #define AND_MEDIA_KEY_CSD0 "csd-0" @@ -49,6 +52,8 @@ #define AND_MEDIA_KEY_ENCODER "encoder" #define AND_MEDIA_KEY_PRIORITY "priority" #define AND_MEDIA_KEY_STRIDE "stride" +#define AND_MEDIA_KEY_LATENCY "latency" +#define AND_MEDIA_KEY_LOW_LATENCY "low-latency" #define AND_MEDIA_I420_PLANAR_FMT 0x13 #define AND_MEDIA_QUEUE_TIMEOUT 2000*100 @@ -67,15 +72,18 @@ /* Maximum duration from one key frame to the next (in seconds). */ #define KEYFRAME_INTERVAL 1 -#define CODEC_WAIT_RETRY 10 -#define CODEC_THREAD_WAIT 10 -/* Timeout until the buffer is ready in ms. */ -#define CODEC_DEQUEUE_TIMEOUT 20 - #define AND_MED_H264_PT PJMEDIA_RTP_PT_H264_RSV2 #define AND_MED_VP8_PT PJMEDIA_RTP_PT_VP8_RSV1 #define AND_MED_VP9_PT PJMEDIA_RTP_PT_VP9_RSV1 +#define BUFFER_MAX_ITEM 16 + +typedef struct and_med_buf_info { + pj_int32_t index; + pj_int32_t size; + pj_uint32_t flags; +} and_med_buf_info; + /* * Factory operations. */ @@ -205,6 +213,15 @@ typedef struct and_media_codec_data unsigned dec_stride_len; unsigned dec_buf_size; AMediaCodecBufferInfo dec_buf_info; + + pj_atomic_queue_t *enc_avail_input_buf; + pj_atomic_queue_t *enc_avail_output_buf; + pj_atomic_queue_t *dec_avail_input_buf; + pj_atomic_queue_t *dec_avail_output_buf; + + pj_bool_t format_changed; + pjmedia_rect_size new_size; + int new_stride; } and_media_codec_data; /* Custom callbacks. */ @@ -234,6 +251,93 @@ typedef pj_status_t(*decode_cb)(pjmedia_vid_codec *codec, unsigned out_size, pjmedia_frame *output); +/** + * Called when an input buffer becomes available. + * The specified index is the index of the available input buffer. + */ +static void and_med_on_input_avail(AMediaCodec *codec, + void *userdata, + int32_t index) +{ + and_media_codec_data *and_media_data = (and_media_codec_data *) userdata; + and_med_buf_info buf_info; + pj_atomic_queue_t *buf_queue; + + pj_bzero(&buf_info, sizeof(buf_info)); + if (codec == and_media_data->enc) { + buf_queue = and_media_data->enc_avail_input_buf; + } else { + buf_queue = and_media_data->dec_avail_input_buf; + } + buf_info.index = index; + pj_atomic_queue_put(buf_queue, &buf_info); +} +/** + * Called when an output buffer becomes available. + * The specified index is the index of the available output buffer. + */ +static void and_med_on_output_avail(AMediaCodec *codec, + void *userdata, + int32_t index, + AMediaCodecBufferInfo *bufferInfo) +{ + and_media_codec_data *and_media_data = (and_media_codec_data *) userdata; + and_med_buf_info buf_info; + pj_atomic_queue_t *buf_queue; + + pj_bzero(&buf_info, sizeof(buf_info)); + if (codec == and_media_data->enc) { + buf_queue = and_media_data->enc_avail_output_buf; + } else { + buf_queue = and_media_data->dec_avail_output_buf; + } + buf_info.index = index; + buf_info.size = bufferInfo->size; + buf_info.flags = bufferInfo->flags; + pj_atomic_queue_put(buf_queue, &buf_info); +} + +/** + * Called when the output format has changed. + * The specified format contains the new output format. + */ +static void and_med_on_format_changed(AMediaCodec *codec, + void *userdata, + AMediaFormat *format) +{ + int width, height, stride; + and_media_codec_data *and_media_data = (and_media_codec_data *) userdata; + + AMediaFormat_getInt32(format, AND_MEDIA_KEY_WIDTH, &width); + AMediaFormat_getInt32(format, AND_MEDIA_KEY_HEIGHT, &height); + AMediaFormat_getInt32(format, AND_MEDIA_KEY_STRIDE, &stride); + if(codec==and_media_data->dec){ + and_media_data->format_changed = PJ_TRUE; + and_media_data->new_size.w = width; + and_media_data->new_size.h = height; + and_media_data->new_stride = stride; + } + __android_log_print(ANDROID_LOG_INFO, THIS_FILE, + "[%s] On format changed w:%d h:%d stride:%d\r\n", + (codec==and_media_data->enc)?"encoder":"decoder", + width, height, stride); +} + +/** + * Called when the MediaCodec encountered an error. + */ +static void and_med_on_error(AMediaCodec *codec, + void *userdata, + media_status_t error, + int32_t actionCode, + const char *detail) +{ + and_media_codec_data *and_media_data = (and_media_codec_data *) userdata; + __android_log_print(ANDROID_LOG_INFO, THIS_FILE, + "[%s] On Media error : err[%d] code[%d] msg[%s]\r\n", + (codec==and_media_data->enc)?"encoder":"decoder", error, + actionCode, detail); +} /* Custom callback implementation. */ #if PJMEDIA_HAS_AND_MEDIA_H264 @@ -373,12 +477,13 @@ static pj_status_t configure_encoder(and_media_codec_data *and_media_data) AMediaFormat_setInt32(vid_fmt, AND_MEDIA_KEY_BIT_RATE, param->enc_fmt.det.vid.avg_bps); //AMediaFormat_setInt32(vid_fmt, AND_MEDIA_KEY_PROFILE, 1); - AMediaFormat_setInt32(vid_fmt, AND_MEDIA_KEY_IFR_INTTERVAL, + AMediaFormat_setInt32(vid_fmt, AND_MEDIA_KEY_IFR_INTERVAL, KEYFRAME_INTERVAL); AMediaFormat_setInt32(vid_fmt, AND_MEDIA_KEY_FRAME_RATE, (param->enc_fmt.det.vid.fps.num / param->enc_fmt.det.vid.fps.denum)); AMediaFormat_setInt32(vid_fmt, AND_MEDIA_KEY_PRIORITY, 0); + AMediaFormat_setInt32(vid_fmt, AND_MEDIA_KEY_LATENCY, 1); /* Configure and start encoder. */ am_status = AMediaCodec_configure(and_media_data->enc, vid_fmt, NULL, NULL, @@ -418,6 +523,7 @@ static pj_status_t configure_decoder(and_media_codec_data *and_media_data) { AMediaFormat_setInt32(vid_fmt, AND_MEDIA_KEY_MAX_INPUT_SZ, 0); AMediaFormat_setInt32(vid_fmt, AND_MEDIA_KEY_ENCODER, 0); AMediaFormat_setInt32(vid_fmt, AND_MEDIA_KEY_PRIORITY, 0); + AMediaFormat_setInt32(vid_fmt, AND_MEDIA_KEY_LOW_LATENCY, 1); if (and_media_codec[and_media_data->codec_idx].fmt_id == PJMEDIA_FORMAT_H264) @@ -805,6 +911,16 @@ static void create_codec(struct and_media_codec_data *and_media_data) if (!and_media_data->enc) { PJ_LOG(4, (THIS_FILE, "Failed creating encoder: %s", enc_name)); } + pj_atomic_queue_create(and_media_data->pool, + BUFFER_MAX_ITEM, + sizeof(and_med_buf_info), + "enc_input_buf", + &and_media_data->enc_avail_input_buf); + pj_atomic_queue_create(and_media_data->pool, + BUFFER_MAX_ITEM, + sizeof(and_med_buf_info), + "enc_output_buf", + &and_media_data->enc_avail_output_buf); } if (!and_media_data->dec) { @@ -812,7 +928,18 @@ static void create_codec(struct and_media_codec_data *and_media_data) if (!and_media_data->dec) { PJ_LOG(4, (THIS_FILE, "Failed creating decoder: %s", dec_name)); } + pj_atomic_queue_create(and_media_data->pool, + BUFFER_MAX_ITEM, + sizeof(and_med_buf_info), + "dec_input_buf", + &and_media_data->dec_avail_input_buf); + pj_atomic_queue_create(and_media_data->pool, + BUFFER_MAX_ITEM, + sizeof(and_med_buf_info), + "dec_output_buf", + &and_media_data->dec_avail_output_buf); } + PJ_LOG(4, (THIS_FILE, "Created encoder: %s, decoder: %s", enc_name, dec_name)); } @@ -886,12 +1013,20 @@ static pj_status_t and_media_dealloc_codec(pjmedia_vid_codec_factory *factory, AMediaCodec_stop(and_media_data->enc); AMediaCodec_delete(and_media_data->enc); and_media_data->enc = NULL; + pj_atomic_queue_destroy(and_media_data->enc_avail_input_buf); + and_media_data->enc_avail_input_buf = NULL; + pj_atomic_queue_destroy(and_media_data->enc_avail_output_buf); + and_media_data->enc_avail_output_buf = NULL; } if (and_media_data->dec) { AMediaCodec_stop(and_media_data->dec); AMediaCodec_delete(and_media_data->dec); and_media_data->dec = NULL; + pj_atomic_queue_destroy(and_media_data->dec_avail_input_buf); + and_media_data->dec_avail_input_buf = NULL; + pj_atomic_queue_destroy(and_media_data->dec_avail_output_buf); + and_media_data->dec_avail_output_buf = NULL; } pj_pool_release(and_media_data->pool); return PJ_SUCCESS; @@ -913,6 +1048,10 @@ static pj_status_t and_media_codec_open(pjmedia_vid_codec *codec, pjmedia_vid_codec_param *param; pj_status_t status = PJ_SUCCESS; + AMediaCodecOnAsyncNotifyCallback async_cb = {&and_med_on_input_avail, + &and_med_on_output_avail, + &and_med_on_format_changed, + &and_med_on_error}; and_media_data = (and_media_codec_data*) codec->codec_data; and_media_data->prm = pjmedia_vid_codec_param_clone( and_media_data->pool, codec_param); @@ -923,6 +1062,11 @@ static pj_status_t and_media_codec_open(pjmedia_vid_codec *codec, if (status != PJ_SUCCESS) return status; } + AMediaCodec_setAsyncNotifyCallback(and_media_data->enc, async_cb, + and_media_data); + AMediaCodec_setAsyncNotifyCallback(and_media_data->dec, async_cb, + and_media_data); + and_media_data->whole = (param->packing == PJMEDIA_VID_PACKING_WHOLE); status = configure_encoder(and_media_data); if (status != PJ_SUCCESS) { @@ -981,13 +1125,18 @@ static pj_status_t and_media_codec_encode_begin(pjmedia_vid_codec *codec, pj_bool_t *has_more) { struct and_media_codec_data *and_media_data; - unsigned i; - pj_ssize_t buf_idx; + and_med_buf_info buf_info; + media_status_t am_status; + pj_size_t output_size; + pj_uint8_t *input_buf = NULL; + pj_uint8_t *output_buf; + pj_atomic_queue_t *queue; PJ_ASSERT_RETURN(codec && input && out_size && output && has_more, PJ_EINVAL); and_media_data = (and_media_codec_data*) codec->codec_data; + pj_bzero(&buf_info, sizeof(buf_info)); if (opt && opt->force_keyframe) { #if __ANDROID_API__ >=26 @@ -1010,145 +1159,114 @@ static pj_status_t and_media_codec_encode_begin(pjmedia_vid_codec *codec, #endif } - buf_idx = AMediaCodec_dequeueInputBuffer(and_media_data->enc, - CODEC_DEQUEUE_TIMEOUT); - if (buf_idx >= 0) { - media_status_t am_status; - pj_size_t output_size; - pj_uint8_t *input_buf = AMediaCodec_getInputBuffer(and_media_data->enc, - buf_idx, &output_size); - if (input_buf && output_size >= input->size) { - pj_memcpy(input_buf, input->buf, input->size); - am_status = AMediaCodec_queueInputBuffer(and_media_data->enc, - buf_idx, 0, input->size, 0, 0); - if (am_status != AMEDIA_OK) { - PJ_LOG(4, (THIS_FILE, "Encoder queueInputBuffer return %d", - am_status)); - goto on_return; - } - } else { - if (!input_buf) { - PJ_LOG(4,(THIS_FILE, "Encoder getInputBuffer " - "returns no input buff")); - } else { - PJ_LOG(4,(THIS_FILE, "Encoder getInputBuffer " - "size: %lu, expecting %lu.", - (unsigned long)output_size, - (unsigned long)input->size)); - } - goto on_return; - } - } else { - PJ_LOG(4,(THIS_FILE, "Encoder dequeueInputBuffer failed[%ld]", - buf_idx)); + queue = and_media_data->enc_avail_input_buf; + if (pj_atomic_queue_get(queue, &buf_info) != PJ_SUCCESS || + buf_info.index < 0) + { + PJ_LOG(4,(THIS_FILE, "Encoder failed to get input Buffer[%d]", + buf_info.index)); goto on_return; } - for (i = 0; i < CODEC_WAIT_RETRY; ++i) { - buf_idx = AMediaCodec_dequeueOutputBuffer(and_media_data->enc, - &and_media_data->enc_buf_info, - CODEC_DEQUEUE_TIMEOUT); - if (buf_idx == -1) { - /* Timeout, wait until output buffer is availble. */ - PJ_LOG(5, (THIS_FILE, "Encoder dequeueOutputBuffer timeout[%d]", - i+1)); - pj_thread_sleep(CODEC_THREAD_WAIT); + input_buf = AMediaCodec_getInputBuffer(and_media_data->enc, + buf_info.index, &output_size); + if (input_buf && output_size >= input->size) { + pj_memcpy(input_buf, input->buf, input->size); + am_status = AMediaCodec_queueInputBuffer(and_media_data->enc, + buf_info.index, 0, input->size, 0, 0); + if (am_status != AMEDIA_OK) { + PJ_LOG(4, (THIS_FILE, "Encoder queueInputBuffer return %d", + am_status)); + goto on_return; + } + } else { + if (!input_buf) { + PJ_LOG(4,(THIS_FILE, "Encoder getInputBuffer " + "returns no input buff")); } else { - break; + PJ_LOG(4,(THIS_FILE, "Encoder getInputBuffer " + "size: %lu, expecting %lu.", + (unsigned long)output_size, + (unsigned long)input->size)); } + goto on_return; } - if (buf_idx >= 0) { - pj_size_t output_size; - pj_uint8_t *output_buf = AMediaCodec_getOutputBuffer( - and_media_data->enc, - buf_idx, - &output_size); - if (!output_buf) { - PJ_LOG(4, (THIS_FILE, "Encoder failed getting output buffer, " - "buffer size %d, offset %d, flags %d", - and_media_data->enc_buf_info.size, - and_media_data->enc_buf_info.offset, - and_media_data->enc_buf_info.flags)); - goto on_return; - } - and_media_data->enc_processed = 0; - and_media_data->enc_frame_whole = output_buf; - and_media_data->enc_output_buf_idx = buf_idx; - and_media_data->enc_frame_size = and_media_data->enc_buf_info.size; + queue = and_media_data->enc_avail_output_buf; + if (pj_atomic_queue_get(queue, &buf_info) != PJ_SUCCESS || + buf_info.index < 0) + { + PJ_LOG(4, (THIS_FILE, "Encoder failed to get output Buffer[%d]", + buf_info.index)); + goto on_return; + } + and_media_data->enc_output_buf_idx = buf_info.index; + and_media_data->enc_buf_info.size = buf_info.size; + and_media_data->enc_buf_info.flags = buf_info.flags; + output_buf = AMediaCodec_getOutputBuffer(and_media_data->enc, + buf_info.index, + &output_size); + if (!output_buf) { + PJ_LOG(4, (THIS_FILE, "Encoder failed getting output buffer, " + "buffer size %d, offset %d, flags %d", + and_media_data->enc_buf_info.size, + and_media_data->enc_buf_info.offset, + and_media_data->enc_buf_info.flags)); + goto on_return; + } + and_media_data->enc_processed = 0; + and_media_data->enc_frame_whole = output_buf; + and_media_data->enc_output_buf_idx = buf_info.index; + and_media_data->enc_frame_size = and_media_data->enc_buf_info.size; - if (and_media_codec[and_media_data->codec_idx].process_encode) { - pj_status_t status; + if (and_media_codec[and_media_data->codec_idx].process_encode) { + pj_status_t status; - status = and_media_codec[and_media_data->codec_idx].process_encode( - and_media_data); + status = and_media_codec[and_media_data->codec_idx].process_encode( + and_media_data); - if (status != PJ_SUCCESS) - goto on_return; - } + if (status != PJ_SUCCESS) + goto on_return; + } - if(and_media_data->enc_buf_info.flags & AND_MEDIA_FRM_TYPE_KEYFRAME) { - output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME; - } + if(and_media_data->enc_buf_info.flags & AND_MEDIA_FRM_TYPE_KEYFRAME) { + output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME; + } - if (and_media_data->whole) { - unsigned payload_size = 0; - unsigned start_data = 0; + if (and_media_data->whole) { + unsigned payload_size = 0; + unsigned start_data = 0; - *has_more = PJ_FALSE; + *has_more = PJ_FALSE; - if ((and_media_data->prm->enc_fmt.id == PJMEDIA_FORMAT_H264) && - (and_media_data->enc_buf_info.flags & - AND_MEDIA_FRM_TYPE_KEYFRAME)) - { - h264_codec_data *h264_data = - (h264_codec_data *)and_media_data->ex_data; - start_data = h264_data->enc_sps_pps_len; - pj_memcpy(output->buf, h264_data->enc_sps_pps_buf, - h264_data->enc_sps_pps_len); - } + if ((and_media_data->prm->enc_fmt.id == PJMEDIA_FORMAT_H264) && + (and_media_data->enc_buf_info.flags & + AND_MEDIA_FRM_TYPE_KEYFRAME)) + { + h264_codec_data *h264_data = + (h264_codec_data *)and_media_data->ex_data; + start_data = h264_data->enc_sps_pps_len; + pj_memcpy(output->buf, h264_data->enc_sps_pps_buf, + h264_data->enc_sps_pps_len); + } - payload_size = and_media_data->enc_buf_info.size + start_data; + payload_size = and_media_data->enc_buf_info.size + start_data; - if (payload_size > out_size) - return PJMEDIA_CODEC_EFRMTOOSHORT; + if (payload_size > out_size) + return PJMEDIA_CODEC_EFRMTOOSHORT; - output->type = PJMEDIA_FRAME_TYPE_VIDEO; - output->size = payload_size; - output->timestamp = input->timestamp; - pj_memcpy((pj_uint8_t*)output->buf+start_data, - and_media_data->enc_frame_whole, - and_media_data->enc_buf_info.size); + output->type = PJMEDIA_FRAME_TYPE_VIDEO; + output->size = payload_size; + output->timestamp = input->timestamp; + pj_memcpy((pj_uint8_t*)output->buf+start_data, + and_media_data->enc_frame_whole, + and_media_data->enc_buf_info.size); - AMediaCodec_releaseOutputBuffer(and_media_data->enc, - buf_idx, - 0); + AMediaCodec_releaseOutputBuffer(and_media_data->enc, + buf_info.index, + 0); - return PJ_SUCCESS; - } - } else { - if (buf_idx == -2) { - int width, height, color_fmt, stride; - - /* Format change. */ - AMediaFormat *vid_fmt = AMediaCodec_getOutputFormat( - and_media_data->enc); - - AMediaFormat_getInt32(vid_fmt, AND_MEDIA_KEY_WIDTH, &width); - AMediaFormat_getInt32(vid_fmt, AND_MEDIA_KEY_HEIGHT, &height); - AMediaFormat_getInt32(vid_fmt, AND_MEDIA_KEY_COLOR_FMT, &color_fmt); - AMediaFormat_getInt32(vid_fmt, AND_MEDIA_KEY_STRIDE, &stride); - PJ_LOG(5, (THIS_FILE, "Encoder detect new width %d, height %d, " - "color_fmt 0x%X, stride %d buf_size %d", - width, height, color_fmt, stride, - and_media_data->enc_buf_info.size)); - - AMediaFormat_delete(vid_fmt); - } else { - PJ_LOG(4, (THIS_FILE, "Encoder dequeueOutputBuffer failed[%ld]", - buf_idx)); - } - goto on_return; + return PJ_SUCCESS; } return and_media_codec_encode_more(codec, out_size, output, has_more); @@ -1236,39 +1354,28 @@ static int write_yuv(pj_uint8_t *buf, return dst - buf; } -static void and_media_get_input_buffer( +static pj_bool_t and_media_get_input_buffer( struct and_media_codec_data *and_media_data) { - pj_ssize_t buf_idx = -1; + and_med_buf_info buf_info; + pj_atomic_queue_t *queue = and_media_data->dec_avail_input_buf; - buf_idx = AMediaCodec_dequeueInputBuffer(and_media_data->dec, - CODEC_DEQUEUE_TIMEOUT); - - if (buf_idx < 0) { - PJ_LOG(4,(THIS_FILE, "Decoder dequeueInputBuffer failed return %ld", - buf_idx)); - - and_media_data->dec_input_buf = NULL; - - if (buf_idx == -10000) { - PJ_LOG(5, (THIS_FILE, "Resetting decoder")); - AMediaCodec_stop(and_media_data->dec); - AMediaCodec_delete(and_media_data->dec); - and_media_data->dec = NULL; - - create_codec(and_media_data); - if (and_media_data->dec) - configure_decoder(and_media_data); - } - return; + pj_bzero(&buf_info, sizeof(buf_info)); + if (pj_atomic_queue_get(queue, &buf_info) != PJ_SUCCESS || + buf_info.index < 0) + { + PJ_LOG(4,(THIS_FILE, "Decoder failed to get input Buffer [%d]", + buf_info.index)); + return PJ_FALSE; } and_media_data->dec_input_buf_len = 0; - and_media_data->dec_input_buf_idx = buf_idx; + and_media_data->dec_input_buf_idx = buf_info.index; and_media_data->dec_input_buf = AMediaCodec_getInputBuffer( and_media_data->dec, - buf_idx, + buf_info.index, &and_media_data->dec_input_buf_max_size); + return PJ_TRUE; } static pj_status_t and_media_decode(pjmedia_vid_codec *codec, @@ -1277,9 +1384,44 @@ static pj_status_t and_media_decode(pjmedia_vid_codec *codec, int buf_flag, pj_timestamp *input_ts, pj_bool_t write_output, pjmedia_frame *output) { - pj_ssize_t buf_idx = 0; pj_status_t status = PJ_SUCCESS; + pj_size_t output_size; + int len; media_status_t am_status; + and_med_buf_info buf_info; + pj_uint8_t *output_buf; + pj_atomic_queue_t *queue; + + pj_bzero(&buf_info, sizeof(buf_info)); + if (and_media_data->format_changed) { + unsigned new_width = and_media_data->new_size.w; + unsigned new_height = and_media_data->new_size.h; + + and_media_data->dec_stride_len = and_media_data->new_stride; + if (new_width != and_media_data->prm->dec_fmt.det.vid.size.w || + new_height != and_media_data->prm->dec_fmt.det.vid.size.h) + { + pjmedia_event event; + + and_media_data->prm->dec_fmt.det.vid.size.w = new_width; + and_media_data->prm->dec_fmt.det.vid.size.h = new_height; + + PJ_LOG(4,(THIS_FILE, "Frame size changed to %dx%d", + and_media_data->prm->dec_fmt.det.vid.size.w, + and_media_data->prm->dec_fmt.det.vid.size.h)); + + /* Broadcast format changed event */ + pjmedia_event_init(&event, PJMEDIA_EVENT_FMT_CHANGED, + NULL, codec); + event.data.fmt_changed.dir = PJMEDIA_DIR_DECODING; + pjmedia_format_copy(&event.data.fmt_changed.new_fmt, + &and_media_data->prm->dec_fmt); + pjmedia_event_publish(NULL, codec, &event, + PJMEDIA_EVENT_PUBLISH_DEFAULT); + } + + and_media_data->format_changed = PJ_FALSE; + } if ((and_media_data->dec_input_buf_max_size > 0) && (and_media_data->dec_input_buf_len + buf_size > @@ -1299,8 +1441,7 @@ static pj_status_t and_media_decode(pjmedia_vid_codec *codec, and_media_data->dec_input_buf = NULL; } - if (and_media_data->dec_input_buf == NULL) - { + if (and_media_data->dec_input_buf == NULL) { and_media_get_input_buffer(and_media_data); if (and_media_data->dec_input_buf == NULL) { @@ -1330,92 +1471,54 @@ static pj_status_t and_media_decode(pjmedia_vid_codec *codec, } and_media_data->dec_input_buf_len += buf_size; - buf_idx = AMediaCodec_dequeueOutputBuffer(and_media_data->dec, - &and_media_data->dec_buf_info, - CODEC_DEQUEUE_TIMEOUT); - - if (buf_idx >= 0) { - pj_size_t output_size; - int len; - - pj_uint8_t *output_buf = AMediaCodec_getOutputBuffer( - and_media_data->dec, - buf_idx, - &output_size); - if (output_buf == NULL) { - am_status = AMediaCodec_releaseOutputBuffer(and_media_data->dec, - buf_idx, - 0); - PJ_LOG(4,(THIS_FILE, "Decoder getOutputBuffer failed")); - return status; - } - len = write_yuv((pj_uint8_t *)output->buf, - output->size, - output_buf, - and_media_data->dec_stride_len, - and_media_data->prm->dec_fmt.det.vid.size.w, - and_media_data->prm->dec_fmt.det.vid.size.h); + pj_bzero(&buf_info, sizeof(buf_info)); + queue = and_media_data->dec_avail_output_buf; + if (pj_atomic_queue_get(queue, &buf_info) != PJ_SUCCESS || + buf_info.index < 0) + { + PJ_LOG(4,(THIS_FILE, "Decoder failed to get output Buffer[%d]", + buf_info.index)); + return status; + } + output_buf = AMediaCodec_getOutputBuffer(and_media_data->dec, + buf_info.index, + &output_size); + if (output_buf == NULL) { am_status = AMediaCodec_releaseOutputBuffer(and_media_data->dec, - buf_idx, - 0); - - if (len > 0) { - if (!and_media_data->dec_has_output_frame) { - output->type = PJMEDIA_FRAME_TYPE_VIDEO; - output->size = len; - output->timestamp = *input_ts; - - and_media_data->dec_has_output_frame = PJ_TRUE; - } - } else { - status = PJMEDIA_CODEC_EFRMTOOSHORT; - } - } else if (buf_idx == -2) { - int width, height, stride; - AMediaFormat *vid_fmt; - /* Get output format */ - vid_fmt = AMediaCodec_getOutputFormat(and_media_data->dec); - - AMediaFormat_getInt32(vid_fmt, AND_MEDIA_KEY_WIDTH, &width); - AMediaFormat_getInt32(vid_fmt, AND_MEDIA_KEY_HEIGHT, &height); - AMediaFormat_getInt32(vid_fmt, AND_MEDIA_KEY_STRIDE, &stride); - - AMediaFormat_delete(vid_fmt); - and_media_data->dec_stride_len = stride; - if (width != and_media_data->prm->dec_fmt.det.vid.size.w || - height != and_media_data->prm->dec_fmt.det.vid.size.h) - { - pjmedia_event event; - - and_media_data->prm->dec_fmt.det.vid.size.w = width; - and_media_data->prm->dec_fmt.det.vid.size.h = height; - - PJ_LOG(4,(THIS_FILE, "Frame size changed to %dx%d", - and_media_data->prm->dec_fmt.det.vid.size.w, - and_media_data->prm->dec_fmt.det.vid.size.h)); + buf_info.index, 0); + PJ_LOG(4,(THIS_FILE, "Decoder getOutputBuffer failed")); + return status; + } + len = write_yuv((pj_uint8_t *)output->buf, + output->size, + output_buf, + and_media_data->dec_stride_len, + and_media_data->prm->dec_fmt.det.vid.size.w, + and_media_data->prm->dec_fmt.det.vid.size.h); + + am_status = AMediaCodec_releaseOutputBuffer(and_media_data->dec, + buf_info.index, 0); + + if (len > 0) { + if (!and_media_data->dec_has_output_frame) { + output->type = PJMEDIA_FRAME_TYPE_VIDEO; + output->size = len; + output->timestamp = *input_ts; - /* Broadcast format changed event */ - pjmedia_event_init(&event, PJMEDIA_EVENT_FMT_CHANGED, - &output->timestamp, codec); - event.data.fmt_changed.dir = PJMEDIA_DIR_DECODING; - pjmedia_format_copy(&event.data.fmt_changed.new_fmt, - &and_media_data->prm->dec_fmt); - pjmedia_event_publish(NULL, codec, &event, - PJMEDIA_EVENT_PUBLISH_DEFAULT); + and_media_data->dec_has_output_frame = PJ_TRUE; } } else { - PJ_LOG(4,(THIS_FILE, "Decoder dequeueOutputBuffer failed [%ld]", - buf_idx)); + status = PJMEDIA_CODEC_EFRMTOOSHORT; } return status; } static pj_status_t and_media_codec_decode(pjmedia_vid_codec *codec, - pj_size_t count, - pjmedia_frame packets[], - unsigned out_size, - pjmedia_frame *output) + pj_size_t count, + pjmedia_frame packets[], + unsigned out_size, + pjmedia_frame *output) { struct and_media_codec_data *and_media_data; pj_status_t status = PJ_EINVAL; From 44b77b6832b4e4f5e15214de48e40af7d85964a0 Mon Sep 17 00:00:00 2001 From: Riza Sulistyo Date: Tue, 29 Oct 2024 15:11:02 +0700 Subject: [PATCH 085/491] Compile time option to enable using original form of SIP URI (#4120) --- pjsip/include/pjsip/sip_config.h | 14 ++++++++++++++ pjsip/include/pjsip/sip_uri.h | 2 ++ pjsip/src/pjsip/sip_parser.c | 5 ++++- pjsip/src/pjsip/sip_uri.c | 7 ++++++- pjsip/src/test/uri_test.c | 2 ++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h index ca7999bf21..504e7561d1 100644 --- a/pjsip/include/pjsip/sip_config.h +++ b/pjsip/include/pjsip/sip_config.h @@ -606,6 +606,20 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void) #endif + /** + * If non-zero, SIP parser will parse the user/password part of the URI, + * and use it in its original form. Otherwise the parser will unescape it, + * and then store and use it in its unescaped form. + * + * To store the URI in the original form, set this to Yes/1. + * + * Default: 0 + */ +#ifndef PJSIP_URI_USE_ORIG_USERPASS +# define PJSIP_URI_USE_ORIG_USERPASS 0 +#endif + + /** * Specify port number should be allowed to appear in To and From * header. Note that RFC 3261 disallow this, see Table 1 in section diff --git a/pjsip/include/pjsip/sip_uri.h b/pjsip/include/pjsip/sip_uri.h index 945ef97325..cf412916b0 100644 --- a/pjsip/include/pjsip/sip_uri.h +++ b/pjsip/include/pjsip/sip_uri.h @@ -339,7 +339,9 @@ typedef struct pjsip_sip_uri pjsip_uri_vptr *vptr; /**< Pointer to virtual function table.*/ pj_str_t user; /**< Optional user part. */ pj_str_t passwd; /**< Optional password part. */ +#if defined (PJSIP_URI_USE_ORIG_USERPASS) && (PJSIP_URI_USE_ORIG_USERPASS) pj_str_t orig_userpass; /**< Optional original user&pass. */ +#endif pj_str_t host; /**< Host part, always exists. */ int port; /**< Optional port number, or zero. */ pj_str_t user_param; /**< Optional user parameter */ diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c index 3da90dc85a..401e13f8bb 100644 --- a/pjsip/src/pjsip/sip_parser.c +++ b/pjsip/src/pjsip/sip_parser.c @@ -1531,14 +1531,17 @@ static void* int_parse_sip_url( pj_scanner *scanner, } if (int_is_next_user(scanner)) { +#if defined (PJSIP_URI_USE_ORIG_USERPASS) && (PJSIP_URI_USE_ORIG_USERPASS) char *start = scanner->curptr; pj_str_t orig; +#endif - start = scanner->curptr; int_parse_user_pass(scanner, pool, &url->user, &url->passwd); +#if defined (PJSIP_URI_USE_ORIG_USERPASS) && (PJSIP_URI_USE_ORIG_USERPASS) pj_strset3(&orig, start, scanner->curptr - 1); pj_strdup(pool, &url->orig_userpass, &orig); +#endif } /* Get host:port */ diff --git a/pjsip/src/pjsip/sip_uri.c b/pjsip/src/pjsip/sip_uri.c index ed28a004cd..b0546c4ac1 100644 --- a/pjsip/src/pjsip/sip_uri.c +++ b/pjsip/src/pjsip/sip_uri.c @@ -269,10 +269,12 @@ static pj_ssize_t pjsip_url_print( pjsip_uri_context_e context, /* Print "user:password@", if any. */ if (url->user.slen) { +#if defined (PJSIP_URI_USE_ORIG_USERPASS) && (PJSIP_URI_USE_ORIG_USERPASS) /* Use the URI's original username and password, if any. */ if (url->orig_userpass.slen) { copy_advance_check(buf, url->orig_userpass); } else { +#endif const pj_cis_t *spec = pjsip_cfg()->endpt.allow_tx_hash_in_uri ? &pc->pjsip_USER_SPEC_LENIENT : &pc->pjsip_USER_SPEC; @@ -281,8 +283,9 @@ static pj_ssize_t pjsip_url_print( pjsip_uri_context_e context, copy_advance_char_check(buf, ':'); copy_advance_escape(buf, url->passwd, pc->pjsip_PASSWD_SPEC); } +#if defined (PJSIP_URI_USE_ORIG_USERPASS) && (PJSIP_URI_USE_ORIG_USERPASS) } - +#endif copy_advance_char_check(buf, '@'); } @@ -513,7 +516,9 @@ PJ_DEF(void) pjsip_sip_uri_assign(pj_pool_t *pool, pjsip_sip_uri *url, { pj_strdup( pool, &url->user, &rhs->user); pj_strdup( pool, &url->passwd, &rhs->passwd); +#if defined (PJSIP_URI_USE_ORIG_USERPASS) && (PJSIP_URI_USE_ORIG_USERPASS) pj_strdup( pool, &url->orig_userpass, &rhs->orig_userpass); +#endif pj_strdup( pool, &url->host, &rhs->host); url->port = rhs->port; pj_strdup( pool, &url->user_param, &rhs->user_param); diff --git a/pjsip/src/test/uri_test.c b/pjsip/src/test/uri_test.c index e4098dc603..fcac088058 100644 --- a/pjsip/src/test/uri_test.c +++ b/pjsip/src/test/uri_test.c @@ -571,7 +571,9 @@ static pjsip_uri *create_uri14(pj_pool_t *pool) pj_strdup2(pool, &name_addr->display, "This is -. !% *_+`'~ me"); pj_strdup2(pool, &url->user, "a19A&=+$,;?/,"); pj_strdup2(pool, &url->passwd, "@a&Zz=+$,"); +#if defined (PJSIP_URI_USE_ORIG_USERPASS) && (PJSIP_URI_USE_ORIG_USERPASS) pj_strdup2(pool, &url->orig_userpass, "a19A&=+$,;?/%2c:%40a&Zz=+$,"); +#endif pj_strdup2(pool, &url->host, "my_proxy09.MY-domain.com"); url->port = 9801; return (pjsip_uri*)name_addr; From cc83544f5690317b85f91a8d655a4697f9090185 Mon Sep 17 00:00:00 2001 From: Mario Ban Date: Tue, 29 Oct 2024 09:20:11 +0100 Subject: [PATCH 086/491] Fix crash on incoming TLS call with only unsupported cipher offered (#4113) --- pjsip/src/pjsua-lib/pjsua_call.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index d099bfe3af..2731d33fc7 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -2045,7 +2045,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) NULL); } - if (call->inv->dlg) { + if (call->inv && call->inv->dlg) { pjsip_inv_terminate(call->inv, sip_err_code, PJ_FALSE); } pjsip_dlg_dec_lock(dlg); From 20a0d09812b35fbd541414935265bbfcc4ac92c2 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 31 Oct 2024 09:15:00 +0700 Subject: [PATCH 087/491] Miscelaneous Coverity fixes (29 Oct 2024) (#4122) --- pjlib/src/pj/atomic_queue.cpp | 2 +- pjmedia/src/pjmedia/stream.c | 5 ----- pjmedia/src/pjmedia/vid_conf.c | 4 +++- pjmedia/src/pjmedia/wav_playlist.c | 15 +++++++++------ 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pjlib/src/pj/atomic_queue.cpp b/pjlib/src/pj/atomic_queue.cpp index 2b839af217..f228b55a48 100644 --- a/pjlib/src/pj/atomic_queue.cpp +++ b/pjlib/src/pj/atomic_queue.cpp @@ -126,7 +126,7 @@ class AtomicQueue { return old_ptr; } - AtomicQueue() {} + AtomicQueue():name_(""){} }; struct pj_atomic_queue_t diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index e4c5f7986b..af36eb433c 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -3077,11 +3077,8 @@ static void stream_on_destroy(void *arg) pjmedia_stream* stream = (pjmedia_stream*)arg; /* This function may be called when stream is partly initialized. */ - if (stream->jb_mutex) - pj_mutex_lock(stream->jb_mutex); /* Free codec. */ - if (stream->codec) { pjmedia_codec_close(stream->codec); pjmedia_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec); @@ -3089,9 +3086,7 @@ static void stream_on_destroy(void *arg) } /* Free mutex */ - if (stream->jb_mutex) { - pj_mutex_unlock(stream->jb_mutex); pj_mutex_destroy(stream->jb_mutex); stream->jb_mutex = NULL; } diff --git a/pjmedia/src/pjmedia/vid_conf.c b/pjmedia/src/pjmedia/vid_conf.c index 2620b21b0b..facbcdba59 100644 --- a/pjmedia/src/pjmedia/vid_conf.c +++ b/pjmedia/src/pjmedia/vid_conf.c @@ -342,7 +342,9 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_destroy(pjmedia_vid_conf *vid_conf) } /* Flush any pending operation (connect, disconnect, etc) */ - handle_op_queue(vid_conf); + if (vid_conf->op_queue && vid_conf->op_queue_free) { + handle_op_queue(vid_conf); + } /* Remove any registered ports (at least to cleanup their pool) */ for (i=0; i < vid_conf->opt.max_slot_cnt; ++i) { diff --git a/pjmedia/src/pjmedia/wav_playlist.c b/pjmedia/src/pjmedia/wav_playlist.c index 7ecbe00c46..3ec099805f 100644 --- a/pjmedia/src/pjmedia/wav_playlist.c +++ b/pjmedia/src/pjmedia/wav_playlist.c @@ -352,9 +352,10 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool_, /* Create fport instance. */ fport = create_file_list_port(pool, port_label); if (!fport) { - status = PJ_ENOMEM; - goto on_error; + PJ_PERROR(4,(THIS_FILE, PJ_ENOMEM, "WAV playlist create failed")); + return PJ_ENOMEM; } + fport->pool = pool; afd = pjmedia_format_get_audio_format_detail(&fport->base.info.fmt, 1); @@ -646,16 +647,18 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool_, on_error: - for (index=0; indexfd_list[index] != 0) - pj_file_close(fport->fd_list[index]); + if (fport->fd_list) { + for (index=0; indexfd_list[index] != 0) + pj_file_close(fport->fd_list[index]); + } } if (pool) pj_pool_release(pool); PJ_PERROR(1,(THIS_FILE, status, - "Failed creating WAV playlist '%s'", + "Failed creating WAV playlist '%.*s'", (int)port_label->slen, port_label->ptr)); return status; From ae3874cf3f094320258851f4f5889686b809375f Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 31 Oct 2024 09:15:26 +0700 Subject: [PATCH 088/491] Removed unnecessary prechecks in async conference bridge operations (#4121) --- pjmedia/src/pjmedia/conference.c | 170 +++++++++++++++------------- pjmedia/src/pjmedia/vid_conf.c | 185 +++++++++++++++++-------------- 2 files changed, 193 insertions(+), 162 deletions(-) diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c index ba3e45dbff..ab525b4add 100644 --- a/pjmedia/src/pjmedia/conference.c +++ b/pjmedia/src/pjmedia/conference.c @@ -284,8 +284,6 @@ typedef enum op_type OP_REMOVE_PORT, OP_CONNECT_PORTS, OP_DISCONNECT_PORTS, - OP_DISCONNECT_PORT_FROM_SOURCES, - OP_DISCONNECT_PORT_FROM_SINKS, } op_type; /* Synchronized operation parameter. */ @@ -351,8 +349,6 @@ static void handle_op_queue(pjmedia_conf *conf) op_connect_ports(conf, &op->param); break; case OP_DISCONNECT_PORTS: - case OP_DISCONNECT_PORT_FROM_SOURCES: - case OP_DISCONNECT_PORT_FROM_SINKS: op_disconnect_ports(conf, &op->param); break; default: @@ -1164,7 +1160,8 @@ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf, { struct conf_port *src_port, *dst_port; pj_bool_t start_sound = PJ_FALSE; - unsigned i; + op_entry *ope; + pj_status_t status = PJ_SUCCESS; /* Check arguments */ PJ_ASSERT_RETURN(conf && src_slotmax_ports && @@ -1187,27 +1184,13 @@ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf, src_port = conf->ports[src_slot]; dst_port = conf->ports[sink_slot]; if (!src_port || !dst_port) { - PJ_PERROR(3,(THIS_FILE, PJ_EINVAL, - "Failed connecting ports, make sure ports are valid")); - pj_mutex_unlock(conf->mutex); - pj_log_pop_indent(); - return PJ_EINVAL; - } - - /* Check if connection has been made */ - for (i=0; ilistener_cnt; ++i) { - if (src_port->listener_slots[i] == sink_slot) { - PJ_LOG(3,(THIS_FILE, "Ports connection %d->%d already exists", - src_slot, sink_slot)); - break; - } + status = PJ_EINVAL; + goto on_return; } /* Queue the operation */ - if (i == src_port->listener_cnt) { - op_entry *ope; - - ope = get_free_op_entry(conf); + ope = get_free_op_entry(conf); + if (ope) { ope->type = OP_CONNECT_PORTS; ope->param.connect_ports.src = src_slot; ope->param.connect_ports.sink = sink_slot; @@ -1216,12 +1199,16 @@ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf, PJ_LOG(4,(THIS_FILE, "Connect ports %d->%d queued", src_slot, sink_slot)); + } else { + status = PJ_ENOMEM; + goto on_return; } /* This is first connection, start clock */ if (conf->connect_cnt == 0) start_sound = 1; +on_return: pj_mutex_unlock(conf->mutex); /* Sound device must be started without mutex, otherwise the @@ -1230,9 +1217,14 @@ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf, if (start_sound) resume_sound(conf); + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Connect ports %d->%d failed", + src_slot, sink_slot)); + } + pj_log_pop_indent(); - return PJ_SUCCESS; + return status; } static void op_connect_ports(pjmedia_conf *conf, const op_param *prm) @@ -1249,7 +1241,8 @@ static void op_connect_ports(pjmedia_conf *conf, const op_param *prm) if (!src_port || !dst_port) { PJ_PERROR(3,(THIS_FILE, PJ_EINVAL, - "Failed connecting ports, make sure ports are valid")); + "Failed connecting %d->%d, make sure ports are valid", + src_slot, sink_slot)); return; } @@ -1289,7 +1282,8 @@ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf, unsigned sink_slot ) { struct conf_port *src_port, *dst_port; - unsigned i; + op_entry *ope; + pj_status_t status = PJ_SUCCESS; /* Check arguments */ PJ_ASSERT_RETURN(conf && src_slotmax_ports && @@ -1306,25 +1300,13 @@ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf, src_port = conf->ports[src_slot]; dst_port = conf->ports[sink_slot]; if (!src_port || !dst_port) { - PJ_PERROR(3,(THIS_FILE, PJ_EINVAL,"Cannot disconnect invalid ports")); - pj_mutex_unlock(conf->mutex); - pj_log_pop_indent(); - return PJ_EINVAL; - } - - /* Check if connection has been made */ - for (i=0; ilistener_cnt; ++i) { - if (src_port->listener_slots[i] == sink_slot) - break; + status = PJ_EINVAL; + goto on_return; } - if (i == src_port->listener_cnt) { - PJ_LOG(3,(THIS_FILE, "Ports connection %d->%d does not exist", - src_slot, sink_slot)); - } else { - op_entry *ope; - - ope = get_free_op_entry(conf); + /* Queue the operation */ + ope = get_free_op_entry(conf); + if (ope) { ope->type = OP_DISCONNECT_PORTS; ope->param.disconnect_ports.src = src_slot; ope->param.disconnect_ports.sink = sink_slot; @@ -1332,12 +1314,22 @@ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf, PJ_LOG(4,(THIS_FILE, "Disconnect ports %d->%d queued", src_slot, sink_slot)); + } else { + status = PJ_ENOMEM; + goto on_return; } +on_return: pj_mutex_unlock(conf->mutex); + + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Disconnect ports %d->%d failed", + src_slot, sink_slot)); + } + pj_log_pop_indent(); - return PJ_SUCCESS; + return status; } static void op_disconnect_ports(pjmedia_conf *conf, @@ -1460,6 +1452,8 @@ pjmedia_conf_disconnect_port_from_sources( pjmedia_conf *conf, unsigned sink_slot) { struct conf_port *dst_port; + op_entry *ope; + pj_status_t status = PJ_SUCCESS; /* Check arguments */ PJ_ASSERT_RETURN(conf && sink_slotmax_ports, PJ_EINVAL); @@ -1473,31 +1467,35 @@ pjmedia_conf_disconnect_port_from_sources( pjmedia_conf *conf, /* Ports must be valid. */ dst_port = conf->ports[sink_slot]; if (!dst_port) { - PJ_PERROR(3,(THIS_FILE, PJ_EINVAL,"Cannot disconnect invalid port")); - pj_mutex_unlock(conf->mutex); - pj_log_pop_indent(); - return PJ_EINVAL; + status = PJ_EINVAL; + goto on_return; } - if (dst_port->transmitter_cnt == 0) { - PJ_LOG(3,(THIS_FILE, "Port %d does not have any transmitter", - sink_slot)); - } else { - op_entry *ope; - - ope = get_free_op_entry(conf); + /* Queue the operation */ + ope = get_free_op_entry(conf); + if (ope) { ope->type = OP_DISCONNECT_PORTS; ope->param.disconnect_ports.src = INVALID_SLOT; ope->param.disconnect_ports.sink = sink_slot; pj_list_push_back(conf->op_queue, ope); PJ_LOG(4,(THIS_FILE, "Disconnect ports any->%d queued", sink_slot)); + } else { + status = PJ_ENOMEM; + goto on_return; } +on_return: pj_mutex_unlock(conf->mutex); + + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Disconnect ports any->%d failed", + sink_slot)); + } + pj_log_pop_indent(); - return PJ_SUCCESS; + return status; } @@ -1509,6 +1507,8 @@ pjmedia_conf_disconnect_port_from_sinks( pjmedia_conf *conf, unsigned src_slot) { struct conf_port *src_port; + op_entry *ope; + pj_status_t status = PJ_SUCCESS; /* Check arguments */ PJ_ASSERT_RETURN(conf && src_slotmax_ports, PJ_EINVAL); @@ -1523,30 +1523,34 @@ pjmedia_conf_disconnect_port_from_sinks( pjmedia_conf *conf, /* Port must be valid. */ src_port = conf->ports[src_slot]; if (!src_port) { - PJ_PERROR(3,(THIS_FILE, PJ_EINVAL,"Cannot disconnect invalid port")); - pj_mutex_unlock(conf->mutex); - return PJ_EINVAL; + status = PJ_EINVAL; + goto on_return; } - if (src_port->listener_cnt == 0) { - PJ_LOG(3,(THIS_FILE, "Port %d does not have any transmitter", - src_slot)); - } else { - op_entry *ope; - - ope = get_free_op_entry(conf); + ope = get_free_op_entry(conf); + if (ope) { ope->type = OP_DISCONNECT_PORTS; ope->param.disconnect_ports.src = src_slot; ope->param.disconnect_ports.sink = INVALID_SLOT; pj_list_push_back(conf->op_queue, ope); PJ_LOG(4,(THIS_FILE, "Disconnect ports %d->any queued", src_slot)); + } else { + status = PJ_ENOMEM; + goto on_return; } +on_return: pj_mutex_unlock(conf->mutex); + + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Disconnect ports %d->any failed", + src_slot)); + } + pj_log_pop_indent(); - return PJ_SUCCESS; + return status; } @@ -1575,10 +1579,11 @@ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf, { struct conf_port *conf_port; op_entry *ope; + pj_status_t status = PJ_SUCCESS; pj_log_push_indent(); - PJ_LOG(5,(THIS_FILE, "Port %d remove requested", port)); + PJ_LOG(5,(THIS_FILE, "Remove port %d requested", port)); PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL); @@ -1587,24 +1592,33 @@ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf, /* Port must be valid. */ conf_port = conf->ports[port]; if (conf_port == NULL) { - PJ_PERROR(3, (THIS_FILE, PJ_EINVAL, "Remove port failed")); - pj_mutex_unlock(conf->mutex); - pj_log_pop_indent(); - return PJ_EINVAL; + status = PJ_EINVAL; + goto on_return; } /* Queue the operation */ ope = get_free_op_entry(conf); - ope->type = OP_REMOVE_PORT; - ope->param.remove_port.port = port; - pj_list_push_back(conf->op_queue, ope); - PJ_LOG(4,(THIS_FILE, "Port %d (%.*s) remove queued", port, - (int)conf_port->name.slen, conf_port->name.ptr)); + if (ope) { + ope->type = OP_REMOVE_PORT; + ope->param.remove_port.port = port; + pj_list_push_back(conf->op_queue, ope); + + PJ_LOG(4,(THIS_FILE, "Remove port %d queued", port)); + } else { + status = PJ_ENOMEM; + goto on_return; + } +on_return: pj_mutex_unlock(conf->mutex); + + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Remove port %d failed", port)); + } + pj_log_pop_indent(); - return PJ_SUCCESS; + return status; } diff --git a/pjmedia/src/pjmedia/vid_conf.c b/pjmedia/src/pjmedia/vid_conf.c index facbcdba59..4eed3463c9 100644 --- a/pjmedia/src/pjmedia/vid_conf.c +++ b/pjmedia/src/pjmedia/vid_conf.c @@ -596,8 +596,11 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_remove_port( pjmedia_vid_conf *vid_conf, { vconf_port *cport; op_entry *ope; + pj_status_t status = PJ_SUCCESS; + + pj_log_push_indent(); - PJ_LOG(5,(THIS_FILE, "Port %d remove requested", slot)); + PJ_LOG(5,(THIS_FILE, "Remove video port %d requested", slot)); PJ_ASSERT_RETURN(vid_conf && slotopt.max_slot_cnt, PJ_EINVAL); @@ -606,22 +609,32 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_remove_port( pjmedia_vid_conf *vid_conf, /* Port must be valid. */ cport = vid_conf->ports[slot]; if (cport == NULL) { - PJ_PERROR(3, (THIS_FILE, PJ_EINVAL, "Remove port failed")); - pj_mutex_unlock(vid_conf->mutex); - return PJ_EINVAL; + status = PJ_EINVAL; + goto on_return; } - PJ_LOG(4,(THIS_FILE, "Video port %d remove queued", slot)); - /* Queue the operation */ ope = get_free_op_entry(vid_conf); - ope->type = OP_REMOVE_PORT; - ope->param.remove_port.port = slot; - pj_list_push_back(vid_conf->op_queue, ope); + if (ope) { + ope->type = OP_REMOVE_PORT; + ope->param.remove_port.port = slot; + pj_list_push_back(vid_conf->op_queue, ope); + PJ_LOG(4,(THIS_FILE, "Remove video port %d queued", slot)); + } else { + status = PJ_ENOMEM; + goto on_return; + } +on_return: pj_mutex_unlock(vid_conf->mutex); - return PJ_SUCCESS; + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Remove video port %d failed", slot)); + } + + pj_log_pop_indent(); + + return status; } @@ -761,10 +774,8 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_connect_port( void *opt) { vconf_port *src_port, *dst_port; - unsigned i; - - PJ_LOG(5,(THIS_FILE, "Connect ports %d->%d requested", - src_slot, sink_slot)); + op_entry *ope; + pj_status_t status = PJ_SUCCESS; /* Check arguments */ PJ_ASSERT_RETURN(vid_conf && @@ -772,6 +783,11 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_connect_port( sink_slotopt.max_slot_cnt, PJ_EINVAL); PJ_UNUSED_ARG(opt); + pj_log_push_indent(); + + PJ_LOG(5,(THIS_FILE, "Connect video ports %d->%d requested", + src_slot, sink_slot)); + pj_mutex_lock(vid_conf->mutex); /* Ports must be valid. */ @@ -780,46 +796,43 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_connect_port( if (!src_port || !src_port->port->get_frame || !dst_port || !dst_port->port->put_frame) { - PJ_LOG(3,(THIS_FILE,"Failed connecting video ports, make sure that " - "source has get_frame() & sink has put_frame()")); - pj_mutex_unlock(vid_conf->mutex); - return PJ_EINVAL; - } - - /* Check if connection has been made */ - for (i=0; ilistener_cnt; ++i) { - if (src_port->listener_slots[i] == sink_slot) - break; + status = PJ_EINVAL; + goto on_return; } /* Queue the operation */ - if (i == src_port->listener_cnt) { - op_entry *ope; - - PJ_LOG(4,(THIS_FILE, "Video connect ports %d->%d queued", - src_slot, sink_slot)); - - ope = get_free_op_entry(vid_conf); + ope = get_free_op_entry(vid_conf); + if (ope) { ope->type = OP_CONNECT_PORTS; ope->param.connect_ports.src = src_slot; ope->param.connect_ports.sink = sink_slot; pj_list_push_back(vid_conf->op_queue, ope); + PJ_LOG(4,(THIS_FILE, "Connect video ports %d->%d queued", + src_slot, sink_slot)); + } else { + status = PJ_ENOMEM; + goto on_return; } /* Start clock (if not yet) */ if (vid_conf->connect_cnt == 0) { - pj_status_t status; status = pjmedia_clock_start(vid_conf->clock); if (status != PJ_SUCCESS) { PJ_PERROR(2, (THIS_FILE, status, "Failed to start clock")); - pj_mutex_unlock(vid_conf->mutex); - return status; + goto on_return; } } +on_return: pj_mutex_unlock(vid_conf->mutex); + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Connect video ports %d->%d failed", + src_slot, sink_slot)); + } - return PJ_SUCCESS; + pj_log_pop_indent(); + + return status; } static void op_connect_ports(pjmedia_vid_conf *vid_conf, @@ -871,65 +884,54 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_disconnect_port( unsigned sink_slot) { vconf_port *src_port, *dst_port; - unsigned i, j; - - PJ_LOG(5,(THIS_FILE, "Disconnect ports %d->%d requested", - src_slot, sink_slot)); + op_entry *ope; + pj_status_t status = PJ_SUCCESS; /* Check arguments */ PJ_ASSERT_RETURN(vid_conf && src_slotopt.max_slot_cnt && sink_slotopt.max_slot_cnt, PJ_EINVAL); + pj_log_push_indent(); + + PJ_LOG(5,(THIS_FILE, "Disconnect video ports %d->%d requested", + src_slot, sink_slot)); + pj_mutex_lock(vid_conf->mutex); /* Ports must be valid. */ src_port = vid_conf->ports[src_slot]; dst_port = vid_conf->ports[sink_slot]; if (!src_port || !dst_port) { - PJ_PERROR(3,(THIS_FILE, PJ_EINVAL, - "Disconnect ports failed, src=0x%p dst=0x%p", - src_port, dst_port)); - pj_mutex_unlock(vid_conf->mutex); - return PJ_EINVAL; + status = PJ_EINVAL; + goto on_return; } - /* Check if connection has been made */ - for (i=0; ilistener_cnt; ++i) { - if (src_port->listener_slots[i] == sink_slot) - break; - } - for (j=0; jtransmitter_cnt; ++j) { - if (dst_port->transmitter_slots[j] == src_slot) - break; - } - - if (i != src_port->listener_cnt && j != dst_port->transmitter_cnt) { - op_entry *ope; - - pj_assert(src_port->listener_cnt > 0 && - src_port->listener_cnt < vid_conf->opt.max_slot_cnt); - pj_assert(dst_port->transmitter_cnt > 0 && - dst_port->transmitter_cnt < vid_conf->opt.max_slot_cnt); - - /* Queue the operation */ - PJ_LOG(4,(THIS_FILE, "Video disconnect ports %d->%d queued", - src_slot, sink_slot)); - - ope = get_free_op_entry(vid_conf); + /* Queue the operation */ + ope = get_free_op_entry(vid_conf); + if (ope) { ope->type = OP_DISCONNECT_PORTS; ope->param.disconnect_ports.src = src_slot; ope->param.disconnect_ports.sink = sink_slot; pj_list_push_back(vid_conf->op_queue, ope); + + PJ_LOG(4,(THIS_FILE, "Disconnect video ports %d->%d queued", + src_slot, sink_slot)); } else { - PJ_PERROR(3,(THIS_FILE, PJ_EINVAL, - "Disconnect ports failed, src=0x%p dst=0x%p", - src_port, dst_port)); + status = PJ_ENOMEM; + goto on_return; } - +on_return: pj_mutex_unlock(vid_conf->mutex); - return PJ_SUCCESS; + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Disconnect video ports %d->%d failed", + src_slot, sink_slot)); + } + + pj_log_pop_indent(); + + return status; } static void op_disconnect_ports(pjmedia_vid_conf *vid_conf, @@ -989,7 +991,6 @@ static void op_disconnect_ports(pjmedia_vid_conf *vid_conf, status = pjmedia_clock_stop(vid_conf->clock); if (status != PJ_SUCCESS) { PJ_PERROR(4, (THIS_FILE, status, "Failed to stop clock")); - return; } } @@ -1510,31 +1511,47 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_update_port( pjmedia_vid_conf *vid_conf, { vconf_port *cport; op_entry *ope; - - PJ_LOG(5,(THIS_FILE, "Update port %d requested", slot)); + pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(vid_conf && slotopt.max_slot_cnt, PJ_EINVAL); + pj_log_push_indent(); + + PJ_LOG(5,(THIS_FILE, "Update video port %d requested", slot)); + pj_mutex_lock(vid_conf->mutex); /* Port must be valid. */ cport = vid_conf->ports[slot]; if (cport == NULL) { - PJ_PERROR(3,(THIS_FILE, PJ_EINVAL, "Update port failed")); - pj_mutex_unlock(vid_conf->mutex); - return PJ_EINVAL; + status = PJ_EINVAL; + goto on_return; } /* Queue the operation */ ope = get_free_op_entry(vid_conf); - ope->type = OP_UPDATE_PORT; - ope->param.update_port.port = slot; - pj_list_push_back(vid_conf->op_queue, ope); + if (ope) { + ope->type = OP_UPDATE_PORT; + ope->param.update_port.port = slot; + pj_list_push_back(vid_conf->op_queue, ope); + + PJ_LOG(4,(THIS_FILE, "Update video port %d queued", slot)); + } else { + status = PJ_ENOMEM; + goto on_return; + } - PJ_LOG(4,(THIS_FILE, "Update port %d queued", slot)); +on_return: pj_mutex_unlock(vid_conf->mutex); - return PJ_SUCCESS; + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Update video port %d failed", + slot)); + } + + pj_log_pop_indent(); + + return status; } @@ -1654,7 +1671,7 @@ static void op_update_port(pjmedia_vid_conf *vid_conf, /* Update cport format info */ cport->format = *new_fmt; - PJ_LOG(4,(THIS_FILE, "Port %d updated", slot)); + PJ_LOG(4,(THIS_FILE, "Video port %d updated", slot)); } From b209c98d473b785ca9e79459bdce12ba5ff763e5 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 7 Nov 2024 10:12:52 +0700 Subject: [PATCH 089/491] Allow application to set ALSA devices manually (#4133) --- pjmedia/include/pjmedia-audiodev/alsa.h | 74 ++++++++++++++++++++ pjmedia/src/pjmedia-audiodev/alsa_dev.c | 92 ++++++++++++++++++++----- 2 files changed, 147 insertions(+), 19 deletions(-) create mode 100644 pjmedia/include/pjmedia-audiodev/alsa.h diff --git a/pjmedia/include/pjmedia-audiodev/alsa.h b/pjmedia/include/pjmedia-audiodev/alsa.h new file mode 100644 index 0000000000..615548bfd3 --- /dev/null +++ b/pjmedia/include/pjmedia-audiodev/alsa.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2024 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJMEDIA_AUDIODEV_ALSA_H__ +#define __PJMEDIA_AUDIODEV_ALSA_H__ + +/** + * @file pjmedia-audiodev/alsa.h + * @brief ALSA Audio Device. + */ + +#include + +/** + * @defgroup PJMED_AUDDEV_ALSA ALSA Audio Device + * @ingroup audio_device_api + * @brief ALSA specific Audio Device API + * @{ + * + * This section describes specific functions for ALSA audio devices. + * Application can use @ref PJMEDIA_AUDIODEV_API API to manipulate + * the ALSA audio device. + * + */ + +PJ_BEGIN_DECL + + +/** + * Manually set ALSA devices. This function will remove all devices registered + * in the factory and register the specified devices. + * + * Note that by default the library will automatically try to enumerate and + * register the ALSA devices during factory initialization. Application can + * override the registered devices using this function. + * + * If application wish to let the library do the device enumeration again, + * just call this function with zero device, i.e: \a count is set to zero. + * + * @param af The ALSA factory, or NULL to use the default. + * @param count The number of ALSA device names. + * @param names The ALSA device names to be registered. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_aud_alsa_set_devices(pjmedia_aud_dev_factory *af, + unsigned count, + const char* names[]); + + + +PJ_END_DECL + + +/** + * @} + */ + +#endif /* __PJMEDIA_AUDIODEV_ALSA_H__ */ + diff --git a/pjmedia/src/pjmedia-audiodev/alsa_dev.c b/pjmedia/src/pjmedia-audiodev/alsa_dev.c index 37346f898e..fd868bd06e 100755 --- a/pjmedia/src/pjmedia-audiodev/alsa_dev.c +++ b/pjmedia/src/pjmedia-audiodev/alsa_dev.c @@ -23,6 +23,7 @@ #include #include #include +#include #if defined(PJMEDIA_AUDIO_DEV_HAS_ALSA) && PJMEDIA_AUDIO_DEV_HAS_ALSA @@ -101,8 +102,13 @@ struct alsa_factory pjmedia_aud_dev_info devs[MAX_DEVICES]; char pb_mixer_name[MAX_MIX_NAME_LEN]; char cap_mixer_name[MAX_MIX_NAME_LEN]; + + unsigned custom_dev_cnt; + pj_str_t custom_dev[MAX_DEVICES]; }; +static pjmedia_aud_dev_factory *default_factory; + struct alsa_stream { pjmedia_aud_stream base; @@ -352,6 +358,9 @@ static pj_status_t alsa_factory_init(pjmedia_aud_dev_factory *f) if (PJ_SUCCESS != status) return status; + if (!default_factory) + default_factory = f; + PJ_LOG(4,(THIS_FILE, "ALSA initialized")); return PJ_SUCCESS; } @@ -362,6 +371,9 @@ static pj_status_t alsa_factory_destroy(pjmedia_aud_dev_factory *f) { struct alsa_factory *af = (struct alsa_factory*)f; + if (default_factory == f) + default_factory = NULL; + if (af->pool) pj_pool_release(af->pool); @@ -387,18 +399,14 @@ static pj_status_t alsa_factory_refresh(pjmedia_aud_dev_factory *f) TRACE_((THIS_FILE, "pjmedia_snd_init: Enumerate sound devices")); - if (af->pool != NULL) { - pj_pool_release(af->pool); - af->pool = NULL; - } - - af->pool = pj_pool_create(af->pf, "alsa_aud", 256, 256, NULL); af->dev_cnt = 0; - /* Enumerate sound devices */ - err = snd_device_name_hint(-1, "pcm", (void***)&hints); - if (err != 0) - return PJMEDIA_EAUD_SYSERR; + if (af->custom_dev_cnt == 0) { + /* Enumerate sound devices */ + err = snd_device_name_hint(-1, "pcm", (void***)&hints); + if (err != 0) + return PJMEDIA_EAUD_SYSERR; + } #if ENABLE_TRACING snd_lib_error_set_handler(alsa_error_handler); @@ -407,15 +415,22 @@ static pj_status_t alsa_factory_refresh(pjmedia_aud_dev_factory *f) snd_lib_error_set_handler(null_alsa_error_handler); #endif - n = hints; - while (*n != NULL) { - char *name = snd_device_name_get_hint(*n, "NAME"); - if (name != NULL) { - if (0 != strcmp("null", name)) - add_dev(af, name); - free(name); + if (af->custom_dev_cnt == 0) { + n = hints; + while (*n != NULL) { + char *name = snd_device_name_get_hint(*n, "NAME"); + if (name != NULL) { + if (0 != strcmp("null", name)) + add_dev(af, name); + free(name); + } + n++; + } + } else { + unsigned i; + for (i = 0; i < af->custom_dev_cnt; ++i) { + add_dev(af, af->custom_dev[i].ptr); } - n++; } /* Get the mixer name */ @@ -426,7 +441,9 @@ static pj_status_t alsa_factory_refresh(pjmedia_aud_dev_factory *f) */ snd_lib_error_set_handler(alsa_error_handler); - err = snd_device_name_free_hint((void**)hints); + if (af->custom_dev_cnt == 0) { + err = snd_device_name_free_hint((void**)hints); + } PJ_LOG(4,(THIS_FILE, "ALSA driver found %d devices", af->dev_cnt)); @@ -1155,4 +1172,41 @@ static pj_status_t alsa_stream_destroy (pjmedia_aud_stream *s) return PJ_SUCCESS; } + +/* + * Manually set ALSA devices. + */ +PJ_DEF(pj_status_t) pjmedia_aud_alsa_set_devices( pjmedia_aud_dev_factory *f, + unsigned count, + const char* names[] ) +{ + struct alsa_factory *af = (struct alsa_factory*)f; + + if (!af) + af = (struct alsa_factory*)default_factory; + + PJ_ASSERT_RETURN(af, PJ_EINVAL); + PJ_ASSERT_RETURN(count <= MAX_DEVICES, PJ_ETOOMANY); + + PJ_LOG(4,(THIS_FILE, "ALSA driver set manually %d devices", count)); + + if (af->pool != NULL) { + pj_pool_release(af->pool); + af->pool = NULL; + } + + if (count > 0) { + unsigned i; + af->pool = pj_pool_create(af->pf, "alsa_custom_dev", 256, 256, NULL); + + for (i = 0; i < count; ++i) { + pj_strdup2_with_null(af->pool, &af->custom_dev[i], names[i]); + } + } + af->custom_dev_cnt = count; + + return pjmedia_aud_dev_refresh(); +} + + #endif /* PJMEDIA_AUDIO_DEV_HAS_ALSA */ From 2e004179969a3430f9b51231b60663287f4843dd Mon Sep 17 00:00:00 2001 From: George Joseph Date: Wed, 6 Nov 2024 20:27:04 -0700 Subject: [PATCH 090/491] Add full support for SHA-256 and SHA-512-256 digest algorithms (#4118) --- pjlib/src/pj/ssl_sock_ossl.c | 1 + pjsip/include/pjsip/sip_auth.h | 224 ++++++++- pjsip/include/pjsip/sip_auth_aka.h | 19 +- pjsip/include/pjsip/sip_config.h | 23 +- pjsip/src/pjsip/sip_auth_aka.c | 10 +- pjsip/src/pjsip/sip_auth_client.c | 734 +++++++++++++++++------------ pjsip/src/pjsip/sip_auth_server.c | 91 +++- 7 files changed, 761 insertions(+), 341 deletions(-) diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c index 3716f4f616..67f386d00d 100644 --- a/pjlib/src/pj/ssl_sock_ossl.c +++ b/pjlib/src/pj/ssl_sock_ossl.c @@ -713,6 +713,7 @@ static pj_status_t init_openssl(void) #if OPENSSL_VERSION_NUMBER < 0x009080ffL /* This is now synonym of SSL_library_init() */ OpenSSL_add_all_algorithms(); + OpenSSL_add_all_digests(); #endif /* Init available ciphers */ diff --git a/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h index fa55830fd7..95c8d136c1 100644 --- a/pjsip/include/pjsip/sip_auth.h +++ b/pjsip/include/pjsip/sip_auth.h @@ -42,12 +42,66 @@ PJ_BEGIN_DECL * @{ */ -/** Length of digest MD5 string. */ +/** + * Length of digest MD5 string. + * \deprecated Use #pjsip_auth_algorithm::digest_str_length instead. + */ #define PJSIP_MD5STRLEN 32 -/** Length of digest SHA256 string. */ +/** + * Length of digest SHA256 string. + * \deprecated Use #pjsip_auth_algorithm::digest_str_length instead. + */ #define PJSIP_SHA256STRLEN 64 +/** + * The length of the buffer needed to contain the largest + * supported algorithm's digest. + */ +#define PJSIP_AUTH_MAX_DIGEST_BUFFER_LENGTH 64 + +/** + * Digest Algorithm Types. + * \warning These entries must remain in order with + * no gaps and with _NOT_SET = 0 and _COUNT as the last entry. + * + * The MD5, SHA-256, and SHA-512/256 algorithms are described + * in RFC 7616 and RFC 8760. + * The AKA algorithms are described in RFC 3310 and RFC 4169 + * and 3GPP TS 33.203. + */ +typedef enum pjsip_auth_algorithm_type +{ + PJSIP_AUTH_ALGORITHM_NOT_SET = 0, /**< Algorithm not set. */ + PJSIP_AUTH_ALGORITHM_MD5, /**< MD5 algorithm. */ + PJSIP_AUTH_ALGORITHM_SHA256, /**< SHA-256 algorithm. */ + PJSIP_AUTH_ALGORITHM_SHA512_256, /**< SHA-512/256 algorithm */ + PJSIP_AUTH_ALGORITHM_AKAV1_MD5, /**< AKA v1 with MD5 algorithm. */ + PJSIP_AUTH_ALGORITHM_AKAV2_MD5, /**< AKA v2 with MD5 algorithm. */ + PJSIP_AUTH_ALGORITHM_COUNT, /**< Number of algorithms. */ +} pjsip_auth_algorithm_type; + + +/** + * Authentication Digest Algorithm + * + * This structure describes a digest algorithm used in + * SIP authentication. + * + */ +typedef struct pjsip_auth_algorithm +{ + pjsip_auth_algorithm_type algorithm_type; /**< Digest algorithm type */ + pj_str_t iana_name; /**< IANA/RFC name used in + SIP headers */ + const char *openssl_name; /**< The name used by OpenSSL's + EVP_get_digestbyname() */ + unsigned digest_length; /**< Length of the raw digest + in bytes */ + unsigned digest_str_length; /**< Length of the digest HEX + representation */ +} pjsip_auth_algorithm; + /** Type of data in the credential information in #pjsip_cred_info. */ typedef enum pjsip_cred_data_type @@ -59,6 +113,13 @@ typedef enum pjsip_cred_data_type } pjsip_cred_data_type; +#define PJSIP_CRED_DATA_PASSWD_MASK 0x000F +#define PJSIP_CRED_DATA_EXT_MASK 0x00F0 + +#define PJSIP_CRED_DATA_IS_AKA(cred) (((cred)->data_type & PJSIP_CRED_DATA_EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) +#define PJSIP_CRED_DATA_IS_PASSWD(cred) (((cred)->data_type & PJSIP_CRED_DATA_PASSWD_MASK) == PJSIP_CRED_DATA_PLAIN_PASSWD) +#define PJSIP_CRED_DATA_IS_DIGEST(cred) (((cred)->data_type & PJSIP_CRED_DATA_PASSWD_MASK) == PJSIP_CRED_DATA_DIGEST) + /** Authentication's quality of protection (qop) type. */ typedef enum pjsip_auth_qop_type { @@ -102,11 +163,14 @@ typedef pj_status_t (*pjsip_cred_cb)(pj_pool_t *pool, /** * This structure describes credential information. * A credential information is a static, persistent information that identifies - * username and password required to authorize to a specific realm. + * credentials required to authorize to a specific realm. * * Note that since PJSIP 0.7.0.1, it is possible to make a credential that is * valid for any realms, by setting the realm to star/wildcard character, * i.e. realm = pj_str("*");. + * + * You should always fill this structure with zeros using PJ_POOL_ZALLOC_T() + * or pj_bzero() before setting any fields. */ struct pjsip_cred_info { @@ -115,9 +179,15 @@ struct pjsip_cred_info challenges. */ pj_str_t scheme; /**< Scheme (e.g. "digest"). */ pj_str_t username; /**< User name. */ - int data_type; /**< Type of data (0 for plaintext passwd). */ + int data_type; /**< Type of data \ref pjsip_cred_data_type */ pj_str_t data; /**< The data, which can be a plaintext password or a hashed digest. */ + /** + * If the data_type is #PJSIP_CRED_DATA_DIGEST and the digest algorithm + * used is not MD5 (the default), then this field MUST be set to the + * appropriate digest algorithm type. + */ + pjsip_auth_algorithm_type algorithm_type; /**< Digest algorithm type */ /** Extended data */ union { @@ -182,7 +252,8 @@ typedef struct pjsip_cached_auth pjsip_cached_auth_hdr cached_hdr;/**< List of cached header for each method. */ #endif - + pjsip_auth_algorithm_type challenge_algorithm_type; /**< Challenge + algorithm */ } pjsip_cached_auth; @@ -208,6 +279,45 @@ typedef struct pjsip_auth_clt_pref } pjsip_auth_clt_pref; +/** + * Get a pjsip_auth_algorithm structure by type. + * + * @param algorithm_type The algorithm type + * + * @return A pointer to a pjsip_auth_algorithm structure + * or NULL if not found. + */ +PJ_DECL(const pjsip_auth_algorithm *) pjsip_auth_get_algorithm_by_type( + pjsip_auth_algorithm_type algorithm_type); + + +/** + * Get a pjsip_auth_algorithm by IANA name. + * + * @param iana_name The IANA name (MD5, SHA-256, SHA-512-256) + * + * @return A pointer to a pjsip_auth_algorithm structure + * or NULL if not found. + */ +PJ_DECL(const pjsip_auth_algorithm *) pjsip_auth_get_algorithm_by_iana_name( + const pj_str_t *iana_name); + + +/** + * Check if a digest algorithm is supported. + * Algorithms that require support from OpenSSL will be checked + * at runtime to determine if they are actually available in + * the current version of OpenSSL. + * + * @param algorithm_type The algorithm type + * + * @return PJ_TRUE if the algorithm is supported, + * PJ_FALSE otherwise. + */ +PJ_DECL(pj_bool_t) pjsip_auth_is_algorithm_supported( + pjsip_auth_algorithm_type algorithm_type); + + /** * Duplicate a client authentication preference setting. * @@ -284,10 +394,12 @@ typedef pj_status_t pjsip_auth_lookup_cred( pj_pool_t *pool, */ typedef struct pjsip_auth_lookup_cred_param { - pj_str_t realm; /**< Realm to find the account. */ - pj_str_t acc_name; /**< Account name to look for. */ - pjsip_rx_data *rdata; /**< Incoming request to be authenticated. */ - + pj_str_t realm; /**< Realm to find the account. */ + pj_str_t acc_name; /**< Account name to look for. */ + pjsip_rx_data *rdata; /**< Incoming request to be + authenticated. */ + pjsip_authorization_hdr *auth_hdr; /**< Authorization header to be + authenticated. */ } pjsip_auth_lookup_cred_param; @@ -549,7 +661,8 @@ PJ_DECL(pj_status_t) pjsip_auth_srv_verify( pjsip_auth_srv *auth_srv, * Add authentication challenge headers to the outgoing response in tdata. * Application may specify its customized nonce and opaque for the challenge, * or can leave the value to NULL to make the function fills them in with - * random characters. + * random characters. The digest algorithm defaults to MD5. If you need + * to specify a different algorithm, use #pjsip_auth_srv_challenge2. * * @param auth_srv The server authentication structure. * @param qop Optional qop value. @@ -569,9 +682,43 @@ PJ_DECL(pj_status_t) pjsip_auth_srv_challenge( pjsip_auth_srv *auth_srv, pjsip_tx_data *tdata); /** - * Helper function to create MD5 digest out of the specified + * Add authentication challenge headers to the outgoing response in tdata. + * Application may specify its customized nonce and opaque for the challenge, + * or can leave the value to NULL to make the function fills them in with + * random characters. + * Application must specify the algorithm to use. + * + * @param auth_srv The server authentication structure. + * @param qop Optional qop value. + * @param nonce Optional nonce value. + * @param opaque Optional opaque value. + * @param stale Stale indication. + * @param tdata The outgoing response message. The response must have + * 401 or 407 response code. + * @param algorithm_type One of the #pjsip_auth_algorithm_type values. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjsip_auth_srv_challenge2(pjsip_auth_srv *auth_srv, + const pj_str_t *qop, + const pj_str_t *nonce, + const pj_str_t *opaque, + pj_bool_t stale, + pjsip_tx_data *tdata, + const pjsip_auth_algorithm_type algorithm_type); + +/** + * Helper function to create a digest out of the specified * parameters. * + * \warning Because of ambiguities in the API, this function + * should only be used for backward compatibility with the + * MD5 digest algorithm. New code should use + * #pjsip_auth_create_digest2 + * + * pjsip_cred_info::data_type must be #PJSIP_CRED_DATA_PLAIN_PASSWD + * or #PJSIP_CRED_DATA_DIGEST. + * * @param result String to store the response digest. This string * must have been preallocated by caller with the * buffer at least PJSIP_MD5STRLEN (32 bytes) in size. @@ -599,6 +746,9 @@ PJ_DECL(pj_status_t) pjsip_auth_create_digest(pj_str_t *result, /** * Helper function to create SHA-256 digest out of the specified * parameters. + * \deprecated Use #pjsip_auth_create_digest2 with + * algorithm_type = #PJSIP_AUTH_ALGORITHM_SHA256. + * * * @param result String to store the response digest. This string * must have been preallocated by caller with the @@ -614,7 +764,7 @@ PJ_DECL(pj_status_t) pjsip_auth_create_digest(pj_str_t *result, * * @return PJ_SUCCESS on success. */ -PJ_DEF(pj_status_t) pjsip_auth_create_digestSHA256(pj_str_t* result, +PJ_DECL(pj_status_t) pjsip_auth_create_digestSHA256(pj_str_t* result, const pj_str_t* nonce, const pj_str_t* nc, const pj_str_t* cnonce, @@ -624,6 +774,56 @@ PJ_DEF(pj_status_t) pjsip_auth_create_digestSHA256(pj_str_t* result, const pjsip_cred_info* cred_info, const pj_str_t* method); +/** + * Helper function to create a digest out of the specified + * parameters. + * + * pjsip_cred_info::data_type must be #PJSIP_CRED_DATA_PLAIN_PASSWD + * or #PJSIP_CRED_DATA_DIGEST. + * + * If pjsip_cred_info::data_type is #PJSIP_CRED_DATA_PLAIN_PASSWORD, + * pjsip_cred_info::username + ":" + realm + ":" + pjsip_cred_info::data + * will be hashed using the algorithm_type specified by the last + * parameter passed to this function to create the "ha1" hash. + * + * If pjsip_cred_info::data_type is #PJSIP_CRED_DATA_DIGEST, + * pjsip_cred_info::data must contain the value of + * username + ":" + realm + ":" + password + * pre-hashed with the algorithm specifed by pjsip_cred_info::algorithm_type + * and will be used as the "ha1" hash directly. In this case + * pjsip_cred_info::algorithm_type MUST match the algorithm_type + * passed as the last parameter to this function. + * + * \note If left unset (0), pjsip_cred_info::algorithm_type will + * default to #PJSIP_AUTH_ALGORITHM_MD5. + * + * @param result String to store the response digest. This string + * must have been preallocated by the caller with the + * buffer at least as large as the digest_str_length + * member of the appropriate pjsip_auth_algorithm. + * @param nonce Optional nonce. + * @param nc Nonce count. + * @param cnonce Optional cnonce. + * @param qop Optional qop. + * @param uri URI. + * @param realm Realm. + * @param cred_info Credential info. + * @param method SIP method. + * @param algorithm_type The hash algorithm to use. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjsip_auth_create_digest2(pj_str_t *result, + const pj_str_t *nonce, + const pj_str_t *nc, + const pj_str_t *cnonce, + const pj_str_t *qop, + const pj_str_t *uri, + const pj_str_t *realm, + const pjsip_cred_info *cred_info, + const pj_str_t *method, + const pjsip_auth_algorithm_type algorithm_type); + /** * @} */ diff --git a/pjsip/include/pjsip/sip_auth_aka.h b/pjsip/include/pjsip/sip_auth_aka.h index 41b77e7764..ba3f9ca4ef 100644 --- a/pjsip/include/pjsip/sip_auth_aka.h +++ b/pjsip/include/pjsip/sip_auth_aka.h @@ -56,7 +56,7 @@ PJ_BEGIN_DECL * Application then specifies digest AKA credential by initializing the * authentication credential as follows: * - @code + \verbatim pjsip_cred_info cred; @@ -65,20 +65,19 @@ PJ_BEGIN_DECL cred.scheme = pj_str("Digest"); cred.realm = pj_str("ims-domain.test"); cred.username = pj_str("user@ims-domain.test"); - cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD | PJSIP_CRED_DATA_EXT_AKA; - cred.data = pj_str("password"); + cred.data_type = PJSIP_CRED_DATA_EXT_AKA; // AKA extended info cred.ext.aka.k = pj_str("password"); cred.ext.aka.cb = &pjsip_auth_create_aka_response - @endcode + \endverbatim * * Description: - * - To support AKA, application adds \a PJSIP_CRED_DATA_EXT_AKA flag in the + * - To support AKA, application adds #PJSIP_CRED_DATA_EXT_AKA flag in the * \a data_type field. This indicates that extended information specific to * AKA authentication is available in the credential, and that response - * digest computation will use the callback function instead of the usual MD5 + * digest computation will use the callback function instead of the usual * digest computation. * * - The \a scheme for the credential is "Digest". @@ -87,12 +86,6 @@ PJ_BEGIN_DECL * also specify wildcard realm ("*") if it wishes to respond to any realms * in the challenge. * - * - The \a data field is optional. Application may fill this with the password - * if it wants to support both MD5 and AKA MD5 in a single credential. The - * pjsip_auth_create_aka_response() function will use this field if the - * challenge indicates "MD5" as the algorithm instead of "AKAv1-MD5" or - * "AKAv2-MD5". - * * - The \a ext.aka.k field specifies the permanent subscriber key to be used * for AKA authentication. Application may specify binary password containing * NULL character in this key, since the length of the key is indicated in @@ -164,7 +157,7 @@ PJ_BEGIN_DECL #define PJSIP_AKA_SQNLEN 6 /** - * This function creates MD5, AKAv1-MD5, or AKAv2-MD5 response for + * This function creates an AKAv1-MD5, or AKAv2-MD5 response for * the specified challenge in \a chal, according to the algorithm * specified in the challenge, and based on the information in the * credential \a cred. diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h index 504e7561d1..57a1ebdb69 100644 --- a/pjsip/include/pjsip/sip_config.h +++ b/pjsip/include/pjsip/sip_config.h @@ -1345,10 +1345,25 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void) #endif /** - * Allow client to send multiple Authorization header when receiving multiple - * WWW-Authenticate header fields. If this is disabled, the stack will send - * Authorization header field containing credentials that match the - * topmost header field. + * RFC-7616 and RFC-8760 state that for each realm a UAS requires authentication + * for it can send a WWW/Proxy-Authenticate header for each digest algorithm it + * supports and for each realm, they must be added in most-preferred to least- + * preferred order. The RFCs also state that the UAS MUST NOT send multiple + * WWW/Proxy-Authenticate headers with the same realm and algorithm. + * + * The RFCs also state that the UAC SHOULD respond to the topmost header + * for each realm containing a digest algorithm it supports. Neither RFC + * however, states whether the UAC should send multiple Authorization headers + * for the same realm if it can support multiple digest algorithms. Common + * sense dicates though that the UAC should NOT send additional Authorization + * headers for the same realm once it's already sent a more preferred one. + * The reasoning is simple... If a UAS sends two WWW-Authenticate headers, + * the first for SHA-256 and the second for MD5, a UAC responding to both + * completely defeats the purpose of the UAS sending the more secure SHA-256. + * + * Having said that, if there is some corner case where continuing to send + * additional Authorization headers for the same realm is necessary, then + * this define can be set to 1 to allow it. * * Default is 0 */ diff --git a/pjsip/src/pjsip/sip_auth_aka.c b/pjsip/src/pjsip/sip_auth_aka.c index 1b15e9cbd1..c3d5d8b2fa 100644 --- a/pjsip/src/pjsip/sip_auth_aka.c +++ b/pjsip/src/pjsip/sip_auth_aka.c @@ -147,9 +147,10 @@ PJ_DEF(pj_status_t) pjsip_auth_create_aka_response( aka_cred.data.ptr = (char*)res; aka_cred.data.slen = PJSIP_AKA_RESLEN; - status = pjsip_auth_create_digest(&auth->response, &chal->nonce, + status = pjsip_auth_create_digest2(&auth->response, &chal->nonce, &auth->nc, &auth->cnonce, &auth->qop, - &auth->uri, &chal->realm, &aka_cred, method); + &auth->uri, &chal->realm, &aka_cred, method, + PJSIP_AUTH_ALGORITHM_MD5); } else if (aka_version == 2) { @@ -186,9 +187,10 @@ PJ_DEF(pj_status_t) pjsip_auth_create_aka_response( aka_cred.data.ptr, &len); aka_cred.data.slen = hmac64_len; - status = pjsip_auth_create_digest(&auth->response, &chal->nonce, + status = pjsip_auth_create_digest2(&auth->response, &chal->nonce, &auth->nc, &auth->cnonce, &auth->qop, - &auth->uri, &chal->realm, &aka_cred, method); + &auth->uri, &chal->realm, &aka_cred, method, + PJSIP_AUTH_ALGORITHM_MD5); } else { pj_assert(!"Bug!"); diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c index c2c008a522..b26166ac34 100644 --- a/pjsip/src/pjsip/sip_auth_client.c +++ b/pjsip/src/pjsip/sip_auth_client.c @@ -33,20 +33,18 @@ #include -#if PJ_HAS_SSL_SOCK && PJ_SSL_SOCK_IMP==PJ_SSL_SOCK_IMP_OPENSSL -# if !defined(PJSIP_AUTH_HAS_DIGEST_SHA256) -# define PJSIP_AUTH_HAS_DIGEST_SHA256 1 -# endif -#else -# undef PJSIP_AUTH_HAS_DIGEST_SHA256 -# define PJSIP_AUTH_HAS_DIGEST_SHA256 0 -#endif - -#if PJSIP_AUTH_HAS_DIGEST_SHA256 +#if defined(PJ_HAS_SSL_SOCK) && PJ_SSL_SOCK_IMP==PJ_SSL_SOCK_IMP_OPENSSL +# include # include -# if OPENSSL_VERSION_NUMBER >= 0x30000000L -# include +# include +# include +# include + +# if OPENSSL_VERSION_NUMBER < 0x10100000L +# define EVP_MD_CTX_new() EVP_MD_CTX_create() +# define EVP_MD_CTX_free(ctx) EVP_MD_CTX_destroy(ctx) # endif + # ifdef _MSC_VER # include # if OPENSSL_VERSION_NUMBER >= 0x10100000L @@ -56,11 +54,47 @@ # pragma comment(lib, "ssleay32") # endif # endif -#endif +# define DEFINE_HASH_CONTEXT EVP_MD_CTX* mdctx + +#else +#define HAVE_NO_OPENSSL 1 +#define MD5_DIGEST_LENGTH (PJSIP_MD5STRLEN / 2) +#define SHA256_DIGEST_LENGTH (PJSIP_SHA256STRLEN / 2) /* A macro just to get rid of type mismatch between char and unsigned char */ #define MD5_APPEND(pms,buf,len) pj_md5_update(pms, (const pj_uint8_t*)buf, \ (unsigned)len) +#define EVP_MD char +#define EVP_MD_CTX pj_md5_context; +#define DEFINE_HASH_CONTEXT pj_md5_context pmc; pj_md5_context* mdctx = &pmc + +#define EVP_get_digestbyname(digest_name) (digest_name) +#define EVP_MD_CTX_new(mdctx) &pmc +#define EVP_DigestInit_ex(mdctx, md, _unused) (void)md; pj_md5_init(mdctx) +#define EVP_DigestUpdate(mdctx, data, len) MD5_APPEND(mdctx, data, len) +#define EVP_DigestFinal_ex(mdctx, digest, _unused) pj_md5_final(mdctx, digest) +#define EVP_MD_CTX_free(mdctx) +#endif + +const pjsip_auth_algorithm pjsip_auth_algorithms[] = { +/* TYPE IANA name OpenSSL name */ +/* Raw digest byte length Hex representation length */ + { PJSIP_AUTH_ALGORITHM_NOT_SET, {"", 0}, "", + 0, 0}, + { PJSIP_AUTH_ALGORITHM_MD5, {"MD5", 3}, "MD5", + MD5_DIGEST_LENGTH, MD5_DIGEST_LENGTH * 2}, + { PJSIP_AUTH_ALGORITHM_SHA256, {"SHA-256", 7}, "SHA256", + SHA256_DIGEST_LENGTH, SHA256_DIGEST_LENGTH * 2}, + { PJSIP_AUTH_ALGORITHM_SHA512_256, {"SHA-512-256", 11}, "SHA512-256", + SHA256_DIGEST_LENGTH, SHA256_DIGEST_LENGTH * 2}, + { PJSIP_AUTH_ALGORITHM_AKAV1_MD5, {"AKAv1-MD5", 9}, "", + MD5_DIGEST_LENGTH, MD5_DIGEST_LENGTH * 2}, + { PJSIP_AUTH_ALGORITHM_AKAV2_MD5, {"AKAv2-MD5", 9}, "", + MD5_DIGEST_LENGTH, MD5_DIGEST_LENGTH * 2}, + { PJSIP_AUTH_ALGORITHM_COUNT, {"", 0}, "", + 0, 0}, +}; + /* Logging. */ #define THIS_FILE "sip_auth_client.c" @@ -70,9 +104,6 @@ # define AUTH_TRACE_(expr) #endif -#define PASSWD_MASK 0x000F -#define EXT_MASK 0x00F0 - static void dup_bin(pj_pool_t *pool, pj_str_t *dst, const pj_str_t *src) { @@ -96,8 +127,9 @@ PJ_DEF(void) pjsip_cred_info_dup(pj_pool_t *pool, pj_strdup_with_null(pool, &dst->scheme, &src->scheme); pj_strdup_with_null(pool, &dst->username, &src->username); pj_strdup_with_null(pool, &dst->data, &src->data); + dst->algorithm_type = src->algorithm_type; - if ((dst->data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) { + if (PJSIP_CRED_DATA_IS_AKA(dst)) { dup_bin(pool, &dst->ext.aka.k, &src->ext.aka.k); dup_bin(pool, &dst->ext.aka.op, &src->ext.aka.op); dup_bin(pool, &dst->ext.aka.amf, &src->ext.aka.amf); @@ -120,8 +152,10 @@ PJ_DEF(int) pjsip_cred_info_cmp(const pjsip_cred_info *cred1, if (result) goto on_return; result = (cred1->data_type != cred2->data_type); if (result) goto on_return; + result = cred1->algorithm_type != cred2->algorithm_type; + if (result) goto on_return; - if ((cred1->data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) { + if (PJSIP_CRED_DATA_IS_AKA(cred1)) { result = pj_strcmp(&cred1->ext.aka.k, &cred2->ext.aka.k); if (result) goto on_return; result = pj_strcmp(&cred1->ext.aka.op, &cred2->ext.aka.op); @@ -162,158 +196,103 @@ static void digestNtoStr(const unsigned char digest[], int n, char *output) * Create response digest based on the parameters and store the * digest ASCII in 'result'. */ -PJ_DEF(pj_status_t) pjsip_auth_create_digest( pj_str_t *result, - const pj_str_t *nonce, - const pj_str_t *nc, - const pj_str_t *cnonce, - const pj_str_t *qop, - const pj_str_t *uri, - const pj_str_t *realm, - const pjsip_cred_info *cred_info, - const pj_str_t *method) +PJ_DEF(pj_status_t) pjsip_auth_create_digest2( pj_str_t *result, + const pj_str_t *nonce, + const pj_str_t *nc, + const pj_str_t *cnonce, + const pj_str_t *qop, + const pj_str_t *uri, + const pj_str_t *realm, + const pjsip_cred_info *cred_info, + const pj_str_t *method, + const pjsip_auth_algorithm_type algorithm_type) { - char ha1[PJSIP_MD5STRLEN]; - char ha2[PJSIP_MD5STRLEN]; - unsigned char digest[16]; - pj_md5_context pms; + const pjsip_auth_algorithm *algorithm = NULL; + unsigned digest_len = 0; + unsigned digest_strlen = 0; + char ha1[PJSIP_AUTH_MAX_DIGEST_BUFFER_LENGTH * 2]; + char ha2[PJSIP_AUTH_MAX_DIGEST_BUFFER_LENGTH * 2]; + unsigned char digest[PJSIP_AUTH_MAX_DIGEST_BUFFER_LENGTH]; + unsigned dig_len = 0; + const EVP_MD* md; + DEFINE_HASH_CONTEXT; - pj_assert(result->slen >= PJSIP_MD5STRLEN); + PJ_ASSERT_RETURN(result && nonce && uri && realm && cred_info && method, PJ_EINVAL); + pj_bzero(result->ptr, result->slen); - AUTH_TRACE_((THIS_FILE, "Begin creating digest")); + algorithm = pjsip_auth_get_algorithm_by_type(algorithm_type == PJSIP_AUTH_ALGORITHM_NOT_SET + ? PJSIP_AUTH_ALGORITHM_MD5 + : algorithm_type); - if ((cred_info->data_type & PASSWD_MASK) == PJSIP_CRED_DATA_PLAIN_PASSWD) { - /*** - *** ha1 = MD5(username ":" realm ":" password) - ***/ - pj_md5_init(&pms); - MD5_APPEND( &pms, cred_info->username.ptr, cred_info->username.slen); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, realm->ptr, realm->slen); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, cred_info->data.ptr, cred_info->data.slen); - pj_md5_final(&pms, digest); - - digestNtoStr(digest, 16, ha1); - - } else if ((cred_info->data_type & PASSWD_MASK) == PJSIP_CRED_DATA_DIGEST) { - if (cred_info->data.slen != 32) { - pj_assert(!"Invalid cred_info data length"); - pj_bzero(result->ptr, result->slen); - result->slen = 0; - return PJ_EINVAL; - } - pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen ); - } else { - pj_assert(!"Invalid data_type"); - pj_bzero(result->ptr, result->slen); - result->slen = 0; - return PJ_EINVAL; + if (!algorithm) { + PJ_LOG(4, (THIS_FILE, "The algorithm_type is invalid")); + return PJ_ENOTSUP; } - AUTH_TRACE_((THIS_FILE, " ha1=%.32s", ha1)); - - /*** - *** ha2 = MD5(method ":" req_uri) - ***/ - pj_md5_init(&pms); - MD5_APPEND( &pms, method->ptr, method->slen); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, uri->ptr, uri->slen); - pj_md5_final(&pms, digest); - digestNtoStr(digest, 16, ha2); - - AUTH_TRACE_((THIS_FILE, " ha2=%.32s", ha2)); - - /*** - *** When qop is not used: - *** response = MD5(ha1 ":" nonce ":" ha2) - *** - *** When qop=auth is used: - *** response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2) - ***/ - pj_md5_init(&pms); - MD5_APPEND( &pms, ha1, PJSIP_MD5STRLEN); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, nonce->ptr, nonce->slen); - if (qop && qop->slen != 0) { - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, nc->ptr, nc->slen); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, cnonce->ptr, cnonce->slen); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, qop->ptr, qop->slen); + if (!pjsip_auth_is_algorithm_supported(algorithm->algorithm_type)) { + PJ_LOG(4, (THIS_FILE, + "The algorithm (%.*s) referenced by algorithm_type is not supported", + (int)algorithm->iana_name.slen, algorithm->iana_name.ptr)); + return PJ_ENOTSUP; } - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, ha2, PJSIP_MD5STRLEN); - - /* This is the final response digest. */ - pj_md5_final(&pms, digest); - - /* Convert digest to string and store in chal->response. */ - result->slen = PJSIP_MD5STRLEN; - digestNtoStr(digest, 16, result->ptr); - - AUTH_TRACE_((THIS_FILE, " digest=%.32s", result->ptr)); - AUTH_TRACE_((THIS_FILE, "Digest created")); - return PJ_SUCCESS; -} + if (qop && !(nc && cnonce)) { + PJ_LOG(4, (THIS_FILE, "nc and cnonce are required if qop is specified")); + return PJ_EINVAL; + } -/* - * Create response SHA-256 digest based on the parameters and store the - * digest ASCII in 'result'. - */ -PJ_DEF(pj_status_t) pjsip_auth_create_digestSHA256(pj_str_t *result, - const pj_str_t *nonce, - const pj_str_t *nc, - const pj_str_t *cnonce, - const pj_str_t *qop, - const pj_str_t *uri, - const pj_str_t *realm, - const pjsip_cred_info *cred_info, - const pj_str_t *method) -{ -#if PJSIP_AUTH_HAS_DIGEST_SHA256 + digest_len = algorithm->digest_length; + digest_strlen = algorithm->digest_str_length; + dig_len = digest_len; - char ha1[PJSIP_SHA256STRLEN]; - char ha2[PJSIP_SHA256STRLEN]; - unsigned char digest[32]; + if (result->slen < digest_strlen) { + PJ_LOG(4, (THIS_FILE, + "The length of the result buffer must be at least %d bytes " + "for algorithm %.*s", digest_strlen, + (int)algorithm->iana_name.slen, algorithm->iana_name.ptr)); + return PJ_EINVAL; + } + result->slen = 0; -#if OPENSSL_VERSION_NUMBER < 0x30000000L - SHA256_CTX pms; -#else - EVP_MD_CTX* mdctx; - const EVP_MD* md; - unsigned dig_len; -#endif + if (!PJSIP_CRED_DATA_IS_PASSWD(cred_info) && !PJSIP_CRED_DATA_IS_DIGEST(cred_info)) { + PJ_LOG(4, (THIS_FILE, + "cred_info->data_type must be PJSIP_CRED_DATA_PLAIN_PASSWD " + "or PJSIP_CRED_DATA_DIGEST")); + return PJ_EINVAL; + } - pj_assert(result->slen >= PJSIP_SHA256STRLEN); + if (PJSIP_CRED_DATA_IS_DIGEST(cred_info)) { + if (cred_info->algorithm_type != algorithm_type) { + PJ_LOG(4,(THIS_FILE, + "The algorithm specified in the cred_info (%.*s) " + "doesn't match the algorithm requested for hashing (%.*s)", + (int)pjsip_auth_algorithms[cred_info->algorithm_type].iana_name.slen, + pjsip_auth_algorithms[cred_info->algorithm_type].iana_name.ptr, + (int)pjsip_auth_algorithms[algorithm_type].iana_name.slen, + pjsip_auth_algorithms[algorithm_type].iana_name.ptr)); + return PJ_EINVAL; + } + PJ_ASSERT_RETURN(cred_info->data.slen >= digest_strlen, PJ_EINVAL); + } -#if OPENSSL_VERSION_NUMBER >= 0x30000000L - md = EVP_get_digestbyname("SHA256"); + md = EVP_get_digestbyname(algorithm->openssl_name); if (md == NULL) { + /* Shouldn't happen since it was checked above */ return PJ_ENOTSUP; } -#endif - AUTH_TRACE_((THIS_FILE, "Begin creating digest")); + AUTH_TRACE_((THIS_FILE, "Begin creating %.*s digest", + (int)algorithm->iana_name.slen, algorithm->iana_name.ptr)); - if ((cred_info->data_type & PASSWD_MASK) == PJSIP_CRED_DATA_PLAIN_PASSWD) + if (PJSIP_CRED_DATA_IS_PASSWD(cred_info)) { + AUTH_TRACE_((THIS_FILE, " Using plain text password for %.*s digest", + (int)algorithm->iana_name.slen, algorithm->iana_name.ptr)); /*** - *** ha1 = SHA256(username ":" realm ":" password) + *** ha1 = (digest)(username ":" realm ":" password) ***/ -#if OPENSSL_VERSION_NUMBER < 0x30000000L - SHA256_Init(&pms); - SHA256_Update( &pms, cred_info->username.ptr, - cred_info->username.slen); - SHA256_Update( &pms, ":", 1); - SHA256_Update( &pms, realm->ptr, realm->slen); - SHA256_Update( &pms, ":", 1); - SHA256_Update( &pms, cred_info->data.ptr, cred_info->data.slen); - SHA256_Final(digest, &pms); -#else mdctx = EVP_MD_CTX_new(); + EVP_DigestInit_ex(mdctx, md, NULL); EVP_DigestUpdate(mdctx, cred_info->username.ptr, cred_info->username.slen); EVP_DigestUpdate(mdctx, ":", 1); @@ -323,37 +302,19 @@ PJ_DEF(pj_status_t) pjsip_auth_create_digestSHA256(pj_str_t *result, EVP_DigestFinal_ex(mdctx, digest, &dig_len); EVP_MD_CTX_free(mdctx); -#endif - digestNtoStr(digest, 32, ha1); + digestNtoStr(digest, dig_len, ha1); - } else if ((cred_info->data_type & PASSWD_MASK) == PJSIP_CRED_DATA_DIGEST) - { - if (cred_info->data.slen != 64) { - pj_assert(!"Invalid cred_info data length"); - pj_bzero(result->ptr, result->slen); - result->slen = 0; - return PJ_EINVAL; - } - pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen ); } else { - pj_assert(!"Invalid data_type"); - pj_bzero(result->ptr, result->slen); - result->slen = 0; - return PJ_EINVAL; + AUTH_TRACE_((THIS_FILE, " Using pre computed digest for %.*s digest", + (int)algorithm->iana_name.slen, algorithm->iana_name.ptr)); + pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen ); } - AUTH_TRACE_((THIS_FILE, " ha1=%.64s", ha1)); + AUTH_TRACE_((THIS_FILE, " ha1=%.*s", algorithm->digest_str_length, ha1)); /*** - *** ha2 = SHA256(method ":" req_uri) + *** ha2 = (digest)(method ":" req_uri) ***/ -#if OPENSSL_VERSION_NUMBER < 0x30000000L - SHA256_Init(&pms); - SHA256_Update( &pms, method->ptr, method->slen); - SHA256_Update( &pms, ":", 1); - SHA256_Update( &pms, uri->ptr, uri->slen); - SHA256_Final( digest, &pms); -#else mdctx = EVP_MD_CTX_new(); EVP_DigestInit_ex(mdctx, md, NULL); EVP_DigestUpdate(mdctx, method->ptr, method->slen); @@ -361,40 +322,20 @@ PJ_DEF(pj_status_t) pjsip_auth_create_digestSHA256(pj_str_t *result, EVP_DigestUpdate(mdctx, uri->ptr, uri->slen); EVP_DigestFinal_ex(mdctx, digest, &dig_len); EVP_MD_CTX_free(mdctx); -#endif - digestNtoStr(digest, 32, ha2); + digestNtoStr(digest, dig_len, ha2); - AUTH_TRACE_((THIS_FILE, " ha2=%.64s", ha2)); + AUTH_TRACE_((THIS_FILE, " ha2=%.*s", algorithm->digest_str_length, ha2)); /*** *** When qop is not used: - *** response = SHA256(ha1 ":" nonce ":" ha2) + *** response = (digest)(ha1 ":" nonce ":" ha2) *** *** When qop=auth is used: - *** response = SHA256(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2) + *** response = (digest)(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2) ***/ -#if OPENSSL_VERSION_NUMBER < 0x30000000L - SHA256_Init(&pms); - SHA256_Update( &pms, ha1, PJSIP_SHA256STRLEN); - SHA256_Update( &pms, ":", 1); - SHA256_Update( &pms, nonce->ptr, nonce->slen); - if (qop && qop->slen != 0) { - SHA256_Update( &pms, ":", 1); - SHA256_Update( &pms, nc->ptr, nc->slen); - SHA256_Update( &pms, ":", 1); - SHA256_Update( &pms, cnonce->ptr, cnonce->slen); - SHA256_Update( &pms, ":", 1); - SHA256_Update( &pms, qop->ptr, qop->slen); - } - SHA256_Update( &pms, ":", 1); - SHA256_Update( &pms, ha2, PJSIP_SHA256STRLEN); - - /* This is the final response digest. */ - SHA256_Final(digest, &pms); -#else mdctx = EVP_MD_CTX_new(); EVP_DigestInit_ex(mdctx, md, NULL); - EVP_DigestUpdate(mdctx, ha1, PJSIP_SHA256STRLEN); + EVP_DigestUpdate(mdctx, ha1, digest_strlen); EVP_DigestUpdate(mdctx, ":", 1); EVP_DigestUpdate(mdctx, nonce->ptr, nonce->slen); if (qop && qop->slen != 0) { @@ -406,30 +347,138 @@ PJ_DEF(pj_status_t) pjsip_auth_create_digestSHA256(pj_str_t *result, EVP_DigestUpdate(mdctx, qop->ptr, qop->slen); } EVP_DigestUpdate(mdctx, ":", 1); - EVP_DigestUpdate(mdctx, ha2, PJSIP_SHA256STRLEN); + EVP_DigestUpdate(mdctx, ha2, digest_strlen); EVP_DigestFinal_ex(mdctx, digest, &dig_len); EVP_MD_CTX_free(mdctx); -#endif + /* Convert digest to string and store in chal->response. */ - result->slen = PJSIP_SHA256STRLEN; - digestNtoStr(digest, 32, result->ptr); + result->slen = digest_strlen; + digestNtoStr(digest, digest_len, result->ptr); - AUTH_TRACE_((THIS_FILE, " digest=%.64s", result->ptr)); - AUTH_TRACE_((THIS_FILE, "Digest created")); + AUTH_TRACE_((THIS_FILE, "%.*s digest=%.*s", + (int)algorithm->iana_name.slen, algorithm->iana_name.ptr, + (int)result->slen, result->ptr)); + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pjsip_auth_create_digest( pj_str_t *result, + const pj_str_t *nonce, + const pj_str_t *nc, + const pj_str_t *cnonce, + const pj_str_t *qop, + const pj_str_t *uri, + const pj_str_t *realm, + const pjsip_cred_info *cred_info, + const pj_str_t *method) +{ + PJ_ASSERT_RETURN(cred_info, PJ_EINVAL); + PJ_ASSERT_RETURN(!PJSIP_CRED_DATA_IS_AKA(cred_info), PJ_EINVAL); + + return pjsip_auth_create_digest2(result, nonce, nc, cnonce, + qop, uri, realm, cred_info, method, + PJSIP_AUTH_ALGORITHM_MD5); +} + + +/* + * Create response SHA-256 digest based on the parameters and store the + * digest ASCII in 'result'. + * \deprecated Use pjsip_auth_create_digest2 with + * algorithm_type = PJSIP_AUTH_ALGORITHM_SHA256. + */ +PJ_DEF(pj_status_t) pjsip_auth_create_digestSHA256(pj_str_t *result, + const pj_str_t *nonce, + const pj_str_t *nc, + const pj_str_t *cnonce, + const pj_str_t *qop, + const pj_str_t *uri, + const pj_str_t *realm, + const pjsip_cred_info *cred_info, + const pj_str_t *method) +{ + PJ_ASSERT_RETURN(cred_info, PJ_EINVAL); + PJ_ASSERT_RETURN(!PJSIP_CRED_DATA_IS_AKA(cred_info), PJ_EINVAL); + + return pjsip_auth_create_digest2(result, nonce, nc, cnonce, + qop, uri, realm, cred_info, method, + PJSIP_AUTH_ALGORITHM_SHA256); +} + + +PJ_DEF(const pjsip_auth_algorithm *) pjsip_auth_get_algorithm_by_type( + pjsip_auth_algorithm_type algorithm_type) +{ + if (algorithm_type > PJSIP_AUTH_ALGORITHM_NOT_SET + && algorithm_type < PJSIP_AUTH_ALGORITHM_COUNT) { + return &pjsip_auth_algorithms[algorithm_type]; + } + return NULL; +} + + +PJ_DEF(const pjsip_auth_algorithm *) pjsip_auth_get_algorithm_by_iana_name( + const pj_str_t *iana_name) +{ + int i; + + if (!iana_name) { + return NULL; + } + + if (iana_name->slen == 0) { + return &pjsip_auth_algorithms[PJSIP_AUTH_ALGORITHM_MD5]; + } + +#ifdef HAVE_NO_OPENSSL + i = PJSIP_AUTH_ALGORITHM_MD5; + if (pj_stricmp(iana_name, &pjsip_auth_algorithms[i].iana_name) == 0) { + return &pjsip_auth_algorithms[i]; + } #else - PJ_UNUSED_ARG(result); - PJ_UNUSED_ARG(nonce); - PJ_UNUSED_ARG(nc); - PJ_UNUSED_ARG(cnonce); - PJ_UNUSED_ARG(qop); - PJ_UNUSED_ARG(uri); - PJ_UNUSED_ARG(realm); - PJ_UNUSED_ARG(cred_info); - PJ_UNUSED_ARG(method); + for (i = PJSIP_AUTH_ALGORITHM_NOT_SET + 1; i < PJSIP_AUTH_ALGORITHM_COUNT; i++) { + if (pj_stricmp(iana_name, &pjsip_auth_algorithms[i].iana_name) == 0) { + return &pjsip_auth_algorithms[i]; + } + } +#endif + return NULL; +} + + +PJ_DEF(pj_bool_t) pjsip_auth_is_algorithm_supported( + pjsip_auth_algorithm_type algorithm_type) +{ + const pjsip_auth_algorithm *algorithm = NULL; + + if (algorithm_type <= PJSIP_AUTH_ALGORITHM_NOT_SET + || algorithm_type >= PJSIP_AUTH_ALGORITHM_COUNT) { + return PJ_FALSE; + } + algorithm = &pjsip_auth_algorithms[algorithm_type]; + + /* + * If the openssl_name is empty there's no need to check + * if OpenSSL supports it. + */ + if (algorithm->openssl_name[0] == '\0') { + return PJ_TRUE; + } + +#ifdef HAVE_NO_OPENSSL + return (algorithm_type == PJSIP_AUTH_ALGORITHM_MD5); +#else + { + const EVP_MD* md; + md = EVP_get_digestbyname(algorithm->openssl_name); + if (md == NULL) { + return PJ_FALSE; + } + return PJ_TRUE; + } #endif - return PJ_SUCCESS; } @@ -471,7 +520,7 @@ static pj_bool_t has_auth_qop( pj_pool_t *pool, const pj_str_t *qop_offer) * password and method param) should be supplied in the argument. * * The resulting digest will be stored in cred->response. - * The pool is used to allocate 32 bytes to store the digest in cred->response. + * The pool is used to allocate enough bytes to store the digest in cred->response. */ static pj_status_t respond_digest( pj_pool_t *pool, pjsip_digest_credential *cred, @@ -480,31 +529,13 @@ static pj_status_t respond_digest( pj_pool_t *pool, const pjsip_cred_info *cred_info, const pj_str_t *cnonce, pj_uint32_t nc, - const pj_str_t *method) + const pj_str_t *method, + const pjsip_auth_algorithm_type challenge_algorithm_type) { - const pj_str_t pjsip_AKAv1_MD5_STR = { "AKAv1-MD5", 9 }; - pj_bool_t algo_sha256 = PJ_FALSE; pj_status_t status = PJ_SUCCESS; - /* Check if algo is sha256 */ -#if PJSIP_AUTH_HAS_DIGEST_SHA256 - algo_sha256 = (pj_stricmp(&chal->algorithm, &pjsip_SHA256_STR)==0); -#endif - - /* Check algorithm is supported. We support MD5, AKAv1-MD5, and SHA256. */ - if (chal->algorithm.slen==0 || - (algo_sha256 || - pj_stricmp(&chal->algorithm, &pjsip_MD5_STR)==0 || - pj_stricmp(&chal->algorithm, &pjsip_AKAv1_MD5_STR)==0)) - { - PJ_LOG(4,(THIS_FILE, "Digest algorithm is \"%.*s\"", - (int)chal->algorithm.slen, chal->algorithm.ptr)); - } - else { - PJ_LOG(4,(THIS_FILE, "Unsupported digest algorithm \"%.*s\"", - (int)chal->algorithm.slen, chal->algorithm.ptr)); - return PJSIP_EINVALIDALGORITHM; - } + AUTH_TRACE_((THIS_FILE, "Begin responding to %.*s challenge", + (int)chal->algorithm.slen, chal->algorithm.ptr)); /* Build digest credential from arguments. */ pj_strdup(pool, &cred->username, &cred_info->username); @@ -515,29 +546,23 @@ static pj_status_t respond_digest( pj_pool_t *pool, pj_strdup(pool, &cred->opaque, &chal->opaque); /* Allocate memory. */ - cred->response.slen = algo_sha256? PJSIP_SHA256STRLEN : PJSIP_MD5STRLEN; + cred->response.slen = pjsip_auth_algorithms[challenge_algorithm_type].digest_str_length; cred->response.ptr = (char*) pj_pool_alloc(pool, cred->response.slen); if (chal->qop.slen == 0) { /* Server doesn't require quality of protection. */ - if ((cred_info->data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) { + if (PJSIP_CRED_DATA_IS_AKA(cred_info)) { /* Call application callback to create the response digest */ return (*cred_info->ext.aka.cb)(pool, chal, cred_info, method, cred); } else { /* Convert digest to string and store in chal->response. */ - if (algo_sha256) { - status = pjsip_auth_create_digestSHA256( - &cred->response, &cred->nonce, NULL, - NULL, NULL, uri, &chal->realm, - cred_info, method); - } else { - status = pjsip_auth_create_digest( &cred->response, - &cred->nonce, NULL, NULL, NULL, uri, - &chal->realm, cred_info, method); - } + status = pjsip_auth_create_digest2( + &cred->response, &cred->nonce, NULL, + NULL, NULL, uri, &chal->realm, + cred_info, method, challenge_algorithm_type); } } else if (has_auth_qop(pool, &chal->qop)) { @@ -555,27 +580,19 @@ static pj_status_t respond_digest( pj_pool_t *pool, pj_strdup(pool, &cred->cnonce, &dummy_cnonce); } - if ((cred_info->data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) { + if (PJSIP_CRED_DATA_IS_AKA(cred_info)) { /* Call application callback to create the response digest */ return (*cred_info->ext.aka.cb)(pool, chal, cred_info, method, cred); } else { /* Convert digest to string and store in chal->response. */ - if (algo_sha256) { - status = pjsip_auth_create_digestSHA256( - &cred->response, &cred->nonce, - &cred->nc, &cred->cnonce, - &pjsip_AUTH_STR, uri, - &chal->realm, cred_info, - method); - } else { - status = pjsip_auth_create_digest( &cred->response, - &cred->nonce, &cred->nc, - &cred->cnonce, &pjsip_AUTH_STR, - uri, &chal->realm, - cred_info, method); - } + status = pjsip_auth_create_digest2( + &cred->response, &cred->nonce, + &cred->nc, &cred->cnonce, + &pjsip_AUTH_STR, uri, + &chal->realm, cred_info, + method, challenge_algorithm_type); } } else { @@ -689,11 +706,13 @@ static void update_digest_session( pjsip_cached_auth *cached_auth, /* Find cached authentication in the list for the specified realm. */ static pjsip_cached_auth *find_cached_auth( pjsip_auth_clt_sess *sess, - const pj_str_t *realm ) + const pj_str_t *realm, + pjsip_auth_algorithm_type algorithm_type) { pjsip_cached_auth *auth = sess->cached_auth.next; while (auth != &sess->cached_auth) { - if (pj_stricmp(&auth->realm, realm) == 0) + if (pj_stricmp(&auth->realm, realm) == 0 + && auth->challenge_algorithm_type == algorithm_type) return auth; auth = auth->next; } @@ -704,7 +723,8 @@ static pjsip_cached_auth *find_cached_auth( pjsip_auth_clt_sess *sess, /* Find credential to use for the specified realm and auth scheme. */ static const pjsip_cred_info* auth_find_cred( const pjsip_auth_clt_sess *sess, const pj_str_t *realm, - const pj_str_t *auth_scheme) + const pj_str_t *auth_scheme, + const pjsip_auth_algorithm_type algorithm_type) { unsigned i; int wildcard = -1; @@ -712,6 +732,32 @@ static const pjsip_cred_info* auth_find_cred( const pjsip_auth_clt_sess *sess, PJ_UNUSED_ARG(auth_scheme); for (i=0; icred_cnt; ++i) { + switch(sess->cred_info[i].data_type) { + case PJSIP_CRED_DATA_PLAIN_PASSWD: + /* PLAIN_PASSWD creds can be used for any algorithm other than AKA */ + if (algorithm_type != PJSIP_AUTH_ALGORITHM_AKAV1_MD5 + || algorithm_type != PJSIP_AUTH_ALGORITHM_AKAV2_MD5) { + break; + } + continue; + case PJSIP_CRED_DATA_DIGEST: + /* Digest creds can only be used if the algorithms match */ + if (sess->cred_info[i].algorithm_type == algorithm_type) { + break; + } + continue; + case PJSIP_CRED_DATA_EXT_AKA: + /* AKA creds can only be used for AKA algorithm */ + if (algorithm_type == PJSIP_AUTH_ALGORITHM_AKAV1_MD5 + || algorithm_type == PJSIP_AUTH_ALGORITHM_AKAV2_MD5) { + break; + } + continue; + } + /* + * We've determined that the credential can be used for the + * specified algorithm, now check the realm. + */ if (pj_stricmp(&sess->cred_info[i].realm, realm) == 0) return &sess->cred_info[i]; else if (sess->cred_info[i].realm.slen == 1 && @@ -789,6 +835,15 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_clone( pj_pool_t *pool, &rhs->cred_info[i].username); sess->cred_info[i].data_type = rhs->cred_info[i].data_type; pj_strdup(pool, &sess->cred_info[i].data, &rhs->cred_info[i].data); + if (PJSIP_CRED_DATA_IS_DIGEST(&rhs->cred_info[i])) { + sess->cred_info[i].algorithm_type = rhs->cred_info[i].algorithm_type; + } + if (PJSIP_CRED_DATA_IS_AKA(&rhs->cred_info[i])) { + pj_strdup(pool, &sess->cred_info[i].ext.aka.k, &rhs->cred_info[i].ext.aka.k); + pj_strdup(pool, &sess->cred_info[i].ext.aka.op, &rhs->cred_info[i].ext.aka.op); + pj_strdup(pool, &sess->cred_info[i].ext.aka.amf, &rhs->cred_info[i].ext.aka.amf); + sess->cred_info[i].ext.aka.cb = rhs->cred_info[i].ext.aka.cb; + } } /* TODO note: @@ -823,7 +878,7 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess, /* When data_type is PJSIP_CRED_DATA_EXT_AKA, * callback must be specified. */ - if ((c[i].data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) { + if (PJSIP_CRED_DATA_IS_AKA(&c[i])) { #if !PJSIP_HAS_DIGEST_AKA_AUTH if (!PJSIP_HAS_DIGEST_AKA_AUTH) { @@ -860,6 +915,7 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess, pj_strdup(sess->pool, &sess->cred_info[i].realm, &c[i].realm); pj_strdup(sess->pool, &sess->cred_info[i].username, &c[i].username); pj_strdup(sess->pool, &sess->cred_info[i].data, &c[i].data); + sess->cred_info[i].algorithm_type = c[i].algorithm_type; } sess->cred_cnt = cred_cnt; } @@ -909,7 +965,8 @@ static pj_status_t auth_respond( pj_pool_t *req_pool, const pjsip_method *method, pj_pool_t *sess_pool, pjsip_cached_auth *cached_auth, - pjsip_authorization_hdr **p_h_auth) + pjsip_authorization_hdr **p_h_auth, + const pjsip_auth_algorithm_type challenge_algorithm_type) { pjsip_authorization_hdr *hauth; char tmp[PJSIP_MAX_URL_SIZE]; @@ -968,7 +1025,7 @@ static pj_status_t auth_respond( pj_pool_t *req_pool, hauth->scheme = pjsip_DIGEST_STR; status = respond_digest( pool, &hauth->credential.digest, &hdr->challenge.digest, &uri_str, cred_info, - cnonce, nc, &method->name); + cnonce, nc, &method->name, challenge_algorithm_type); if (status != PJ_SUCCESS) return status; @@ -1041,14 +1098,15 @@ static pj_status_t new_auth_for_req( pjsip_tx_data *tdata, PJ_ASSERT_RETURN(tdata && sess && auth, PJ_EINVAL); PJ_ASSERT_RETURN(auth->last_chal != NULL, PJSIP_EAUTHNOPREVCHAL); - cred = auth_find_cred( sess, &auth->realm, &auth->last_chal->scheme ); + cred = auth_find_cred( sess, &auth->realm, &auth->last_chal->scheme, + auth->challenge_algorithm_type ); if (!cred) return PJSIP_ENOCREDENTIAL; status = auth_respond( tdata->pool, auth->last_chal, tdata->msg->line.req.uri, cred, &tdata->msg->line.req.method, - sess->pool, auth, &hauth); + sess->pool, auth, &hauth, auth->challenge_algorithm_type); if (status != PJ_SUCCESS) return status; @@ -1063,15 +1121,50 @@ static pj_status_t new_auth_for_req( pjsip_tx_data *tdata, /* Find credential in list of (Proxy-)Authorization headers */ -static pjsip_authorization_hdr* get_header_for_realm(const pjsip_hdr *hdr_list, - const pj_str_t *realm) +static pjsip_authorization_hdr* get_header_for_cred_info( + const pjsip_hdr *hdr_list, + const pjsip_cred_info *cred_info) { pjsip_authorization_hdr *h; h = (pjsip_authorization_hdr*)hdr_list->next; while (h != (pjsip_authorization_hdr*)hdr_list) { - if (pj_stricmp(&h->credential.digest.realm, realm)==0) + /* If the realm doesn't match, just skip */ + if (pj_stricmp(&h->credential.digest.realm, &cred_info->realm) != 0) { + h = h->next; + continue; + } + + switch (cred_info->data_type) { + case PJSIP_CRED_DATA_PLAIN_PASSWD: + /* + * If cred_info->data_type is PLAIN_PASSWD, then we can use the header + * regardless of algorithm. + */ return h; + case PJSIP_CRED_DATA_DIGEST: + /* + * If cred_info->data_type is DIGEST, then we need to check if the + * algorithms match. + */ + if (pj_stricmp(&h->credential.digest.algorithm, + &pjsip_auth_algorithms[cred_info->algorithm_type].iana_name) == 0) { + return h; + } + break; + case PJSIP_CRED_DATA_EXT_AKA: + /* + * If cred_info->data_type is EXT_AKA, then we need to check if the + * challenge algorithm is AKAv1-MD5 or AKAv2-MD5. + */ + if (pj_stricmp(&h->credential.digest.algorithm, + &pjsip_auth_algorithms[PJSIP_AUTH_ALGORITHM_AKAV1_MD5].iana_name) == 0 + || pj_stricmp(&h->credential.digest.algorithm, + &pjsip_auth_algorithms[PJSIP_AUTH_ALGORITHM_AKAV2_MD5].iana_name) == 0) { + return h; + } + break; + } h = h->next; } @@ -1148,7 +1241,8 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess, pj_status_t status; cred = auth_find_cred(sess, &auth->realm, - &auth->last_chal->scheme); + &auth->last_chal->scheme, + auth->challenge_algorithm_type); if (!cred) { auth = auth->next; continue; @@ -1158,7 +1252,8 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess, tdata->msg->line.req.uri, cred, &tdata->msg->line.req.method, - sess->pool, auth, &hauth); + sess->pool, auth, &hauth, + auth->challenge_algorithm_type); if (status != PJ_SUCCESS) return status; @@ -1201,7 +1296,7 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess, pjsip_cred_info *c = &sess->cred_info[i]; pjsip_authorization_hdr *h; - h = get_header_for_realm(&added, &c->realm); + h = get_header_for_cred_info(&added, c); if (h) { pj_list_erase(h); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)h); @@ -1223,8 +1318,14 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess, pj_strdup(tdata->pool, &hs->credential.digest.realm, &c->realm); pj_strdup(tdata->pool,&hs->credential.digest.uri, &uri); - pj_strdup(tdata->pool, &hs->credential.digest.algorithm, - &sess->pref.algorithm); + + if (c->algorithm_type == PJSIP_AUTH_ALGORITHM_NOT_SET) { + pj_strdup(tdata->pool, &hs->credential.digest.algorithm, + &sess->pref.algorithm); + } else { + pj_strdup(tdata->pool, &hs->credential.digest.algorithm, + &pjsip_auth_algorithms[c->algorithm_type].iana_name); + } } pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hs); @@ -1270,7 +1371,8 @@ static pj_status_t process_auth( pj_pool_t *req_pool, pjsip_tx_data *tdata, pjsip_auth_clt_sess *sess, pjsip_cached_auth *cached_auth, - pjsip_authorization_hdr **h_auth) + pjsip_authorization_hdr **h_auth, + const pjsip_auth_algorithm_type challenge_algorithm_type) { const pjsip_cred_info *cred; pjsip_authorization_hdr *sent_auth = NULL; @@ -1298,15 +1400,26 @@ static pj_status_t process_auth( pj_pool_t *req_pool, hdr = hdr->next; pj_list_erase(sent_auth); continue; - } else - if (pj_stricmp(&sent_auth->scheme, &pjsip_DIGEST_STR)==0 && - pj_stricmp(&sent_auth->credential.digest.algorithm, - &hchal->challenge.digest.algorithm)!=0) - { - /* Same 'digest' scheme but different algo */ - hdr = hdr->next; - continue; } else { +#if defined(PJSIP_AUTH_ALLOW_MULTIPLE_AUTH_HEADER) && \ + PJSIP_AUTH_ALLOW_MULTIPLE_AUTH_HEADER!=0 + /* + * Keep sending additional headers if the the algorithm + * is different. + * WARNING: See the comment in sip_config.h regarding + * how using this option could be a security risk if + * a header with a more secure digest algorithm has already + * been sent. + */ + if (pj_stricmp(&sent_auth->scheme, &pjsip_DIGEST_STR)==0 && + pj_stricmp(&sent_auth->credential.digest.algorithm, + &hchal->challenge.digest.algorithm)!=0) + { + /* Same 'digest' scheme but different algo */ + hdr = hdr->next; + continue; + } else +#endif /* Found previous authorization attempt */ break; } @@ -1370,22 +1483,26 @@ static pj_status_t process_auth( pj_pool_t *req_pool, /* Find credential to be used for the challenge. */ cred = auth_find_cred( sess, &hchal->challenge.common.realm, - &hchal->scheme); + &hchal->scheme, challenge_algorithm_type); if (!cred) { const pj_str_t *realm = &hchal->challenge.common.realm; + AUTH_TRACE_((THIS_FILE, "No cred for for %.*s", + (int)hchal->challenge.digest.algorithm.slen, hchal->challenge.digest.algorithm.ptr)); + PJ_LOG(4,(THIS_FILE, "Unable to set auth for %s: can not find credential for " - "%.*s/%.*s", + "%.*s/%.*s %.*s", tdata->obj_name, (int)realm->slen, realm->ptr, - (int)hchal->scheme.slen, hchal->scheme.ptr)); + (int)hchal->scheme.slen, hchal->scheme.ptr, + (int)hchal->challenge.digest.algorithm.slen, hchal->challenge.digest.algorithm.ptr)); return PJSIP_ENOCREDENTIAL; } /* Respond to authorization challenge. */ status = auth_respond( req_pool, hchal, uri, cred, &tdata->msg->line.req.method, - sess->pool, cached_auth, h_auth); + sess->pool, cached_auth, h_auth, challenge_algorithm_type); return status; } @@ -1433,6 +1550,7 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess, pjsip_cached_auth *cached_auth; const pjsip_www_authenticate_hdr *hchal; pjsip_authorization_hdr *hauth; + const pjsip_auth_algorithm *algorithm; /* Find WWW-Authenticate or Proxy-Authenticate header. */ while (hdr != &rdata->msg_info.msg->hdr && @@ -1447,10 +1565,29 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess, hchal = (const pjsip_www_authenticate_hdr*)hdr; ++chal_cnt; + /* At the current time, "digest" scheme is the only one supported. */ + if (pj_stricmp(&hchal->scheme, &pjsip_DIGEST_STR) != 0) { + AUTH_TRACE_((THIS_FILE, "Skipped header for scheme %.*s", + (int)hchal->scheme.slen, hchal->scheme.ptr)); + last_auth_err = PJSIP_EINVALIDAUTHSCHEME; + hdr = hdr->next; + continue; + } + + algorithm = pjsip_auth_get_algorithm_by_iana_name(&hchal->challenge.digest.algorithm); + if (!algorithm) { + AUTH_TRACE_((THIS_FILE, "Skipped header for algorithm %.*s", + (int)algorithm->iana_name.slen, algorithm->iana_name.ptr)); + last_auth_err = PJSIP_EINVALIDALGORITHM; + hdr = hdr->next; + continue; + } + /* Find authentication session for this realm, create a new one * if not present. */ - cached_auth = find_cached_auth(sess, &hchal->challenge.common.realm); + cached_auth = find_cached_auth(sess, &hchal->challenge.common.realm, + algorithm->algorithm_type); if (!cached_auth) { cached_auth = PJ_POOL_ZALLOC_T(sess->pool, pjsip_cached_auth); cached_auth->pool = pjsip_endpt_create_pool(sess->endpt, @@ -1460,6 +1597,7 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess, pj_strdup(cached_auth->pool, &cached_auth->realm, &hchal->challenge.common.realm); cached_auth->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE); + cached_auth->challenge_algorithm_type = algorithm->algorithm_type; # if (PJSIP_AUTH_HEADER_CACHING) { pj_list_init(&cached_auth->cached_hdr); @@ -1472,9 +1610,12 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess, * authorization session. */ status = process_auth(tdata->pool, hchal, tdata->msg->line.req.uri, - tdata, sess, cached_auth, &hauth); + tdata, sess, cached_auth, &hauth, algorithm->algorithm_type); if (status != PJ_SUCCESS) { last_auth_err = status; + AUTH_TRACE_((THIS_FILE, "Skipped header for realm %.*s algorithm %.*s", + (int)hchal->challenge.common.realm.slen, hchal->challenge.common.realm.ptr, + (int)algorithm->iana_name.slen, algorithm->iana_name.ptr)); /* Process next header. */ hdr = hdr->next; @@ -1493,11 +1634,6 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess, /* Process next header. */ hdr = hdr->next; auth_cnt++; - -#if defined(PJSIP_AUTH_ALLOW_MULTIPLE_AUTH_HEADER) && \ - PJSIP_AUTH_ALLOW_MULTIPLE_AUTH_HEADER==0 - break; -#endif } /* Check if challenge is present */ diff --git a/pjsip/src/pjsip/sip_auth_server.c b/pjsip/src/pjsip/sip_auth_server.c index 8eee2b51b6..edc3aabb41 100644 --- a/pjsip/src/pjsip/sip_auth_server.c +++ b/pjsip/src/pjsip/sip_auth_server.c @@ -22,9 +22,18 @@ #include #include #include +#include #include #include +/* Logging. */ +#define THIS_FILE "sip_auth_server.c" +#if 0 +# define AUTH_TRACE_(expr) PJ_LOG(3, expr) +#else +# define AUTH_TRACE_(expr) +#endif + /* * Initialize server authorization session data structure to serve the @@ -75,11 +84,38 @@ static pj_status_t pjsip_auth_verify( const pjsip_authorization_hdr *hdr, const pj_str_t *method, const pjsip_cred_info *cred_info ) { + if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) { - char digest_buf[PJSIP_MD5STRLEN]; + const pjsip_digest_credential *dig = &hdr->credential.digest; + const pjsip_auth_algorithm *response_algorithm = + pjsip_auth_get_algorithm_by_iana_name(&dig->algorithm); + char digest_buf[PJSIP_AUTH_MAX_DIGEST_BUFFER_LENGTH * 2]; pj_str_t digest; pj_status_t status; - const pjsip_digest_credential *dig = &hdr->credential.digest; + int result = 0; + + AUTH_TRACE_((THIS_FILE, "Authenticating with realm-digest %.*s-%.*s", + (int)dig->realm.slen, dig->realm.ptr, + (int)dig->algorithm.slen, dig->algorithm.ptr)); + /* + * Check that the response algorithm is supported. + * Since we sent the challenge it should be but + * we check anyway in case a client responds with a bad + * one. + */ + PJ_ASSERT_RETURN(response_algorithm + && pjsip_auth_is_algorithm_supported(response_algorithm->algorithm_type), + PJSIP_EINVALIDALGORITHM); + + /* + * Cred info type must be plain password or digest and if it's digest, + * the algorithm must match the algorithm in the header. + * We don't support the AKAv1-MD5 algorithm as a server. + */ + PJ_ASSERT_RETURN(PJSIP_CRED_DATA_IS_PASSWD(cred_info) + || (PJSIP_CRED_DATA_IS_DIGEST(cred_info) + && cred_info->algorithm_type == response_algorithm->algorithm_type), + PJSIP_EINVALIDALGORITHM); /* Check that username and realm match. * These checks should have been performed before entering this @@ -92,10 +128,10 @@ static pj_status_t pjsip_auth_verify( const pjsip_authorization_hdr *hdr, /* Prepare for our digest calculation. */ digest.ptr = digest_buf; - digest.slen = PJSIP_MD5STRLEN; + digest.slen = response_algorithm->digest_str_length; /* Create digest for comparison. */ - status = pjsip_auth_create_digest(&digest, + status = pjsip_auth_create_digest2(&digest, &hdr->credential.digest.nonce, &hdr->credential.digest.nc, &hdr->credential.digest.cnonce, @@ -103,13 +139,19 @@ static pj_status_t pjsip_auth_verify( const pjsip_authorization_hdr *hdr, &hdr->credential.digest.uri, &cred_info->realm, cred_info, - method ); + method, response_algorithm->algorithm_type); + AUTH_TRACE_((THIS_FILE, "Create digest response: %s", + (status == PJ_SUCCESS ? "success" : "failed"))); if (status != PJ_SUCCESS) return status; /* Compare digest. */ - return (pj_stricmp(&digest, &hdr->credential.digest.response) == 0) ? + result = pj_stricmp(&digest, &hdr->credential.digest.response); + AUTH_TRACE_((THIS_FILE, "Digest comparison: %s", + result ? "failed" : "matched")); + + return (result == 0) ? PJ_SUCCESS : PJSIP_EAUTHINVALIDDIGEST; } else { @@ -133,6 +175,8 @@ PJ_DEF(pj_status_t) pjsip_auth_srv_verify( pjsip_auth_srv *auth_srv, pj_str_t acc_name; pjsip_cred_info cred_info; pj_status_t status; + const pjsip_auth_algorithm *algorithm; + PJ_ASSERT_RETURN(auth_srv && rdata, PJ_EINVAL); PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); @@ -171,7 +215,17 @@ PJ_DEF(pj_status_t) pjsip_auth_srv_verify( pjsip_auth_srv *auth_srv, return PJSIP_EINVALIDAUTHSCHEME; } + algorithm = pjsip_auth_get_algorithm_by_iana_name( + &h_auth->credential.digest.algorithm); + + /* Check that algorithm is supported. */ + if (!algorithm || !pjsip_auth_is_algorithm_supported(algorithm->algorithm_type)) { + return PJSIP_EINVALIDALGORITHM; + } + /* Find the credential information for the account. */ + pj_bzero(&cred_info, sizeof(cred_info)); + if (auth_srv->lookup2) { pjsip_auth_lookup_cred_param param; @@ -179,6 +233,7 @@ PJ_DEF(pj_status_t) pjsip_auth_srv_verify( pjsip_auth_srv *auth_srv, param.realm = auth_srv->realm; param.acc_name = acc_name; param.rdata = rdata; + param.auth_hdr = h_auth; status = (*auth_srv->lookup2)(rdata->tp_info.pool, ¶m, &cred_info); if (status != PJ_SUCCESS) { *status_code = PJSIP_SC_FORBIDDEN; @@ -209,19 +264,27 @@ PJ_DEF(pj_status_t) pjsip_auth_srv_verify( pjsip_auth_srv *auth_srv, * or can leave the value to NULL to make the function fills them in with * random characters. */ -PJ_DEF(pj_status_t) pjsip_auth_srv_challenge( pjsip_auth_srv *auth_srv, +PJ_DEF(pj_status_t) pjsip_auth_srv_challenge2( pjsip_auth_srv *auth_srv, const pj_str_t *qop, const pj_str_t *nonce, const pj_str_t *opaque, pj_bool_t stale, - pjsip_tx_data *tdata) + pjsip_tx_data *tdata, + const pjsip_auth_algorithm_type algorithm_type) { pjsip_www_authenticate_hdr *hdr; char nonce_buf[16]; pj_str_t random; + const pjsip_auth_algorithm *algorithm = + pjsip_auth_get_algorithm_by_type(algorithm_type); PJ_ASSERT_RETURN( auth_srv && tdata, PJ_EINVAL ); + /* Check that algorithm is supported. */ + if (!algorithm || !pjsip_auth_is_algorithm_supported(algorithm_type)) { + return PJSIP_EINVALIDALGORITHM; + } + random.ptr = nonce_buf; random.slen = sizeof(nonce_buf); @@ -235,7 +298,7 @@ PJ_DEF(pj_status_t) pjsip_auth_srv_challenge( pjsip_auth_srv *auth_srv, * Note: only support digest authentication now. */ hdr->scheme = pjsip_DIGEST_STR; - hdr->challenge.digest.algorithm = pjsip_MD5_STR; + pj_strdup(tdata->pool, &hdr->challenge.digest.algorithm, &algorithm->iana_name); if (nonce) { pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, nonce); } else { @@ -261,3 +324,13 @@ PJ_DEF(pj_status_t) pjsip_auth_srv_challenge( pjsip_auth_srv *auth_srv, return PJ_SUCCESS; } +PJ_DEF(pj_status_t) pjsip_auth_srv_challenge( pjsip_auth_srv *auth_srv, + const pj_str_t *qop, + const pj_str_t *nonce, + const pj_str_t *opaque, + pj_bool_t stale, + pjsip_tx_data *tdata) +{ + return pjsip_auth_srv_challenge2(auth_srv, qop, nonce, opaque, + stale, tdata, PJSIP_AUTH_ALGORITHM_MD5); +} From ab7478f6dce1b06c185c10302d3e091766c391e9 Mon Sep 17 00:00:00 2001 From: jimying Date: Thu, 7 Nov 2024 11:58:42 +0800 Subject: [PATCH 091/491] iocp: fix interger overflow (pointer conversion error) (#4134) --- pjlib/src/pj/ioqueue_winnt.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pjlib/src/pj/ioqueue_winnt.c b/pjlib/src/pj/ioqueue_winnt.c index 11b6573bc7..19e568cbc5 100644 --- a/pjlib/src/pj/ioqueue_winnt.c +++ b/pjlib/src/pj/ioqueue_winnt.c @@ -606,7 +606,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool, } /* Associate with IOCP */ - hioq = CreateIoCompletionPort((HANDLE)sock, ioqueue->iocp, (DWORD)rec, 0); + hioq = CreateIoCompletionPort((HANDLE)sock, ioqueue->iocp, (ULONG_PTR)rec, 0); if (!hioq) { pj_lock_release(ioqueue->lock); return PJ_RETURN_OS_ERROR(GetLastError()); @@ -705,7 +705,8 @@ static void decrement_counter(pj_ioqueue_key_t *key) static pj_bool_t poll_iocp( HANDLE hIocp, DWORD dwTimeout, pj_ssize_t *p_bytes, pj_ioqueue_key_t **p_key ) { - DWORD dwBytesTransferred, dwKey; + DWORD dwBytesTransferred; + ULONG_PTR dwKey; generic_overlapped *pOv; pj_ioqueue_key_t *key; pj_ssize_t size_status = -1; @@ -1468,7 +1469,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key, BOOL rc; rc = PostQueuedCompletionStatus(key->ioqueue->iocp, bytes_status, - (long)key, (OVERLAPPED*)op_key ); + (ULONG_PTR)key, (OVERLAPPED*)op_key ); if (rc == FALSE) { return PJ_RETURN_OS_ERROR(GetLastError()); } From c4a1deded08f382db96bd1eed8b88d00511497d1 Mon Sep 17 00:00:00 2001 From: half-left <64613921+half-left@users.noreply.github.com> Date: Thu, 7 Nov 2024 06:59:25 +0300 Subject: [PATCH 092/491] Check for unregistered video factory in lookup_dev(#4129) (#4130) --- pjmedia/src/pjmedia/videodev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pjmedia/src/pjmedia/videodev.c b/pjmedia/src/pjmedia/videodev.c index 1747f61bd1..9e074b6d5d 100644 --- a/pjmedia/src/pjmedia/videodev.c +++ b/pjmedia/src/pjmedia/videodev.c @@ -362,13 +362,13 @@ static pj_status_t lookup_dev(pjmedia_vid_dev_index id, for (i=0; if && drv->cap_dev_idx >= 0) { id = drv->cap_dev_idx; make_global_index(i, &id); break; - } else if (id==PJMEDIA_VID_DEFAULT_RENDER_DEV && + } else if (id==PJMEDIA_VID_DEFAULT_RENDER_DEV && drv->f && drv->rend_dev_idx >= 0) { id = drv->rend_dev_idx; From b386f9428d96e566362fa93ba0edb68f02f8206c Mon Sep 17 00:00:00 2001 From: "R. Jeske" Date: Fri, 8 Nov 2024 09:02:15 +0100 Subject: [PATCH 093/491] add duration support for rfc2833 DTMF (#4124) --- pjmedia/include/pjmedia/stream.h | 20 +++++++++++++++++ pjmedia/src/pjmedia/stream.c | 36 +++++++++++++++++++++++++----- pjsip-apps/src/samples/footprint.c | 1 + pjsip/include/pjsua-lib/pjsua.h | 19 ++++++++++++++++ pjsip/src/pjsua-lib/pjsua_aud.c | 11 +++++++-- pjsip/src/pjsua-lib/pjsua_call.c | 2 +- 6 files changed, 81 insertions(+), 8 deletions(-) diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h index 05231f4197..dc90d62022 100644 --- a/pjmedia/include/pjmedia/stream.h +++ b/pjmedia/include/pjmedia/stream.h @@ -413,6 +413,26 @@ PJ_DECL(pj_status_t) pjmedia_stream_resume(pjmedia_stream *stream, PJ_DECL(pj_status_t) pjmedia_stream_dial_dtmf(pjmedia_stream *stream, const pj_str_t *ascii_digit); +/** + * Transmit DTMF to this stream. The DTMF will be transmitted using + * RTP telephone-events as described in RFC 2833. This operation is + * only valid for audio stream. + * + * @param stream The media stream. + * @param ascii_digit String containing digits to be sent to remote as + * described on RFC 2833 section 3.10. + * If PJMEDIA_HAS_DTMF_FLASH is enabled, character 'R' is + * used to represent the event type 16 (flash) as stated + * in RFC 4730. + * Currently the maximum number of digits are 32. + * @param duration duration of event in ms or 0 to use default + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_stream_dial_dtmf2(pjmedia_stream *stream, + const pj_str_t *ascii_digit, + unsigned duration); + /** * Check if the stream has incoming DTMF digits in the incoming DTMF diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index af36eb433c..bea1e717d9 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -96,6 +96,7 @@ struct dtmf { int event; pj_uint32_t duration; + pj_uint32_t send_duration; int ebit_cnt; /**< # of E bit transmissions */ }; @@ -982,9 +983,27 @@ static void create_dtmf_payload(pjmedia_stream *stream, { pjmedia_rtp_dtmf_event *event; struct dtmf *digit = &stream->tx_dtmf_buf[0]; + unsigned duration = 0; pj_assert(sizeof(pjmedia_rtp_dtmf_event) == 4); + if (digit->send_duration) + { + float ts_modifier = 1.0; +#if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0) + if (stream->codec_param.info.pt == PJMEDIA_RTP_PT_G722) { + ts_modifier = 0.5; + } +#endif + if (!pj_stricmp2(&stream->si.fmt.encoding_name, "opus")) { + ts_modifier = 48000 / stream->codec_param.info.clock_rate; + } + duration = ts_modifier * digit->send_duration * stream->codec_param.info.clock_rate / 1000; + } + else + { + duration = stream->dtmf_duration; + } *first = *last = 0; event = (pjmedia_rtp_dtmf_event*) frame_out->buf; @@ -996,19 +1015,18 @@ static void create_dtmf_payload(pjmedia_stream *stream, } digit->duration += stream->rtp_tx_ts_len_per_pkt; - if (digit->duration >= stream->dtmf_duration) - digit->duration = stream->dtmf_duration; + if (digit->duration >= duration) + digit->duration = duration; event->event = (pj_uint8_t)digit->event; event->e_vol = 10; event->duration = pj_htons((pj_uint16_t)digit->duration); if (forced_last) { - digit->duration = stream->dtmf_duration; + digit->duration = duration; } - if (digit->duration >= stream->dtmf_duration) { - + if (digit->duration >= duration) { event->e_vol |= 0x80; if (++digit->ebit_cnt >= DTMF_EBIT_RETRANSMIT_CNT) { @@ -3380,6 +3398,13 @@ PJ_DEF(pj_status_t) pjmedia_stream_resume( pjmedia_stream *stream, */ PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf( pjmedia_stream *stream, const pj_str_t *digit_char) +{ + return pjmedia_stream_dial_dtmf2(stream, digit_char, 0); +} + +PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf2( pjmedia_stream *stream, + const pj_str_t *digit_char, + unsigned duration) { pj_status_t status = PJ_SUCCESS; @@ -3439,6 +3464,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf( pjmedia_stream *stream, stream->tx_dtmf_buf[stream->tx_dtmf_count+i].event = pt; stream->tx_dtmf_buf[stream->tx_dtmf_count+i].duration = 0; + stream->tx_dtmf_buf[stream->tx_dtmf_count+i].send_duration = duration; stream->tx_dtmf_buf[stream->tx_dtmf_count+i].ebit_cnt = 0; } diff --git a/pjsip-apps/src/samples/footprint.c b/pjsip-apps/src/samples/footprint.c index 1b440d93d5..7d889aa98a 100644 --- a/pjsip-apps/src/samples/footprint.c +++ b/pjsip-apps/src/samples/footprint.c @@ -563,6 +563,7 @@ int dummy_function() pjmedia_stream_pause(NULL, PJMEDIA_DIR_ENCODING); pjmedia_stream_resume(NULL, PJMEDIA_DIR_ENCODING); pjmedia_stream_dial_dtmf(NULL, NULL); + pjmedia_stream_dial_dtmf2(NULL, NULL, 0); pjmedia_stream_check_dtmf(NULL); pjmedia_stream_get_dtmf(NULL, NULL, NULL); #endif diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 0bec35bd8d..7245a6ccc6 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -6191,6 +6191,25 @@ PJ_DECL(pj_status_t) pjsua_call_xfer_replaces(pjsua_call_id call_id, PJ_DECL(pj_status_t) pjsua_call_dial_dtmf(pjsua_call_id call_id, const pj_str_t *digits); +/** + * Send DTMF digits to remote using RFC 2833 payload formats. Use + * #pjsua_call_send_dtmf() to send DTMF using SIP INFO or other method in + * \a pjsua_dtmf_method. App can use \a on_dtmf_digit() or \a on_dtmf_digit2() + * callback to monitor incoming DTMF. + * + * @param call_id Call identification. + * @param digits DTMF string digits to be sent as described on RFC 2833 + * section 3.10. If PJMEDIA_HAS_DTMF_FLASH is enabled, + * character 'R' is used to represent the + * event type 16 (flash) as stated in RFC 4730. + * @param duration duration of event in ms or 0 to use default + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsua_call_dial_dtmf2(pjsua_call_id call_id, + const pj_str_t *digits, + unsigned duration); + /** * Send DTMF digits to remote. Use this method to send DTMF using the method in * \a pjsua_dtmf_method. This method will call #pjsua_call_dial_dtmf() when diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index 756ab8135c..0f8a756f5b 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -244,6 +244,13 @@ PJ_DEF(pj_status_t) pjsua_call_get_stream_stat( pjsua_call_id call_id, */ PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id, const pj_str_t *digits) +{ + return pjsua_call_dial_dtmf2(call_id, digits, 0); +} + +PJ_DEF(pj_status_t) pjsua_call_dial_dtmf2( pjsua_call_id call_id, + const pj_str_t *digits, + unsigned duration) { pjsua_call *call; pjsip_dialog *dlg = NULL; @@ -266,8 +273,8 @@ PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id, goto on_return; } - status = pjmedia_stream_dial_dtmf( - call->media[call->audio_idx].strm.a.stream, digits); + status = pjmedia_stream_dial_dtmf2( + call->media[call->audio_idx].strm.a.stream, digits, duration); on_return: if (dlg) pjsip_dlg_dec_lock(dlg); diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 2731d33fc7..2efa133eb5 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -3777,7 +3777,7 @@ PJ_DEF(pj_status_t) pjsua_call_send_dtmf(pjsua_call_id call_id, get_dtmf_method_name(param->method))); if (param->method == PJSUA_DTMF_METHOD_RFC2833) { - status = pjsua_call_dial_dtmf(call_id, ¶m->digits); + status = pjsua_call_dial_dtmf2(call_id, ¶m->digits, param->duration); } else if (param->method == PJSUA_DTMF_METHOD_SIP_INFO) { const pj_str_t SIP_INFO = pj_str("INFO"); int i; From 2a78b5b1d5a1e224b69d1fac903706dbf39eb680 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Mon, 11 Nov 2024 03:52:48 +0200 Subject: [PATCH 094/491] pcaputil: Fix infinite write on skewed timestamp (#4140) --- pjsip-apps/src/samples/pcaputil.c | 35 ++++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/pjsip-apps/src/samples/pcaputil.c b/pjsip-apps/src/samples/pcaputil.c index cf8e425bce..0cb746c2dc 100644 --- a/pjsip-apps/src/samples/pcaputil.c +++ b/pjsip-apps/src/samples/pcaputil.c @@ -21,6 +21,9 @@ #include #include +/* Ignore gap >30s */ +#define GAP_IGNORE_SECONDS 30 + static const char *USAGE = "pcaputil [options] INPUT OUTPUT\n" "\n" @@ -417,25 +420,27 @@ static void pcap2wav(const struct args *args) /* Fill in the gap (if any) between pkt0 and pkt1 */ ts_gap = pj_ntohl(pkt1.rtp->ts) - pj_ntohl(pkt0.rtp->ts) - samples_cnt; - while (ts_gap >= (long)samples_per_frame) { - pcm_frame.buf = pcm; - pcm_frame.size = samples_per_frame * 2; + if (ts_gap <= param.info.clock_rate * GAP_IGNORE_SECONDS) { /* Ignore gap >30s */ + while (ts_gap >= (long)samples_per_frame) { + pcm_frame.buf = pcm; + pcm_frame.size = samples_per_frame * 2; - if (app.codec->op->recover) { - T( pjmedia_codec_recover(app.codec, (unsigned)pcm_frame.size, - &pcm_frame) ); - } else { - pj_bzero(pcm_frame.buf, pcm_frame.size); - } + if (app.codec->op->recover) { + T( pjmedia_codec_recover(app.codec, (unsigned)pcm_frame.size, + &pcm_frame) ); + } else { + pj_bzero(pcm_frame.buf, pcm_frame.size); + } - if (app.wav) { - T( pjmedia_port_put_frame(app.wav, &pcm_frame) ); - } - if (app.aud_strm) { - T( wait_play(&pcm_frame) ); + if (app.wav) { + T( pjmedia_port_put_frame(app.wav, &pcm_frame) ); + } + if (app.aud_strm) { + T( wait_play(&pcm_frame) ); + } + ts_gap -= samples_per_frame; } - ts_gap -= samples_per_frame; } /* Next */ From c28635a2d3d27317c9965f7b9182063f20ca5a28 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Mon, 11 Nov 2024 08:55:53 +0700 Subject: [PATCH 095/491] Misc coverity fixes (#4141) --- pjsip-apps/src/swig/importsym.py | 280 +++++++++++++++--------------- pjsip-apps/src/swig/symbols.i | 23 ++- pjsip-apps/src/swig/symbols.lst | 38 ++-- pjsip/include/pjsua2/siptypes.hpp | 6 + pjsip/src/pjsip/sip_auth_client.c | 2 +- pjsip/src/pjsua2/siptypes.cpp | 8 +- 6 files changed, 190 insertions(+), 167 deletions(-) diff --git a/pjsip-apps/src/swig/importsym.py b/pjsip-apps/src/swig/importsym.py index 6f549b6bcb..36d96b6658 100644 --- a/pjsip-apps/src/swig/importsym.py +++ b/pjsip-apps/src/swig/importsym.py @@ -1,6 +1,6 @@ # # importsym.py: Import C symbol decls (structs, enums, etc) and write them -# to another file +# to another file # # Copyright (C)2013 Teluu Inc. (http://www.teluu.com) # @@ -16,7 +16,7 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # import pycparser from pycparser import c_generator @@ -24,171 +24,173 @@ import os def which(program): - import os - def is_exe(fpath): - return os.path.isfile(fpath) and os.access(fpath, os.X_OK) - - if sys.platform == 'win32' and not program.endswith(".exe"): - program += ".exe" - - fpath, fname = os.path.split(program) - if fpath: - if is_exe(program): - return program - else: - for path in os.environ["PATH"].split(os.pathsep): - path = path.strip('"') - exe_file = os.path.join(path, program) - if is_exe(exe_file): - return exe_file - return None + import os + def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + + if sys.platform == 'win32' and not program.endswith(".exe"): + program += ".exe" + + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + path = path.strip('"') + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return None # PJ_ROOT_PATH = "../../../" - + # CPP is needed by pycparser. CPP_PATH = which("cpp") if not CPP_PATH: - print 'Error: need to have cpp in PATH' - sys.exit(1) + print 'Error: need to have cpp in PATH' + sys.exit(1) # Hardcoded! # Note for win32: +# - use Windows Command Prompt app, not msys/mingw # - temporarily comment "#include " in pj/sock.h (line ~29) if sys.platform == 'win32': - PYCPARSER_DIR="D:/work/tool/pycparser-master" + PYCPARSER_DIR="D:/work/tool/pycparser-master" elif sys.platform == "linux2": - PYCPARSER_DIR="/home/bennylp/Desktop/opt/src/pycparser-master" + PYCPARSER_DIR="/home/bennylp/Desktop/opt/src/pycparser-master" else: - PYCPARSER_DIR="/Library/Python/2.7/site-packages/pycparser" + PYCPARSER_DIR="/Library/Python/2.7/site-packages/pycparser" if not os.path.exists(PYCPARSER_DIR + '/utils/fake_libc_include'): - print "Error: couldn't find pycparser utils in '%s'" % PYCPARSER_DIR - sys.exit(1) + print "Error: couldn't find pycparser utils in '%s'" % PYCPARSER_DIR + sys.exit(1) # Heading, to be placed before the source files C_HEADING_SECTION = """ -#define PJ_AUTOCONF 1 -#define jmp_buf int +#define PJ_AUTOCONF 1 +#define jmp_buf int #define __attribute__(x) """ # CPP (C preprocessor) settings CPP_CFLAGS = [ - '-I' + PYCPARSER_DIR + '/utils/fake_libc_include', - "-I" + PJ_ROOT_PATH + "pjlib/include", - "-I" + PJ_ROOT_PATH + "pjlib-util/include", - "-I" + PJ_ROOT_PATH + "pjnath/include", - "-I" + PJ_ROOT_PATH + "pjmedia/include", - "-I" + PJ_ROOT_PATH + "pjsip/include" - ] + '-I' + PYCPARSER_DIR + '/utils/fake_libc_include', + "-I" + PJ_ROOT_PATH + "pjlib/include", + "-I" + PJ_ROOT_PATH + "pjlib-util/include", + "-I" + PJ_ROOT_PATH + "pjnath/include", + "-I" + PJ_ROOT_PATH + "pjmedia/include", + "-I" + PJ_ROOT_PATH + "pjsip/include" + ] class SymbolVisitor(pycparser.c_ast.NodeVisitor): - def __init__(self, names): - self.nodeDict = {} - for name in names: - self.nodeDict[name] = None - - def _add(self, node): - if self.nodeDict.has_key(node.name): - self.nodeDict[node.name] = node - - def visit_Struct(self, node): - self._add(node) - - def visit_Enum(self, node): - self._add(node) - - def visit_Typename(self, node): - self._add(node) - - def visit_Typedef(self, node): - self._add(node) - - + def __init__(self, names): + self.nodeDict = {} + for name in names: + self.nodeDict[name] = None + + def _add(self, node): + if self.nodeDict.has_key(node.name): + self.nodeDict[node.name] = node + + def visit_Struct(self, node): + self._add(node) + + def visit_Enum(self, node): + self._add(node) + + def visit_Typename(self, node): + self._add(node) + + def visit_Typedef(self, node): + self._add(node) + + TEMP_FILE="tmpsrc.h" - + class SymbolImporter: - """ - Import C selected declarations from C source file and move it - to another file. - - Parameters: - - listfile Path of file containing list of C source file - and identifier names to be imported. The format - of the listfile is: - - filename name1 name2 name3 - - for example: - - pj/sock_qos.h pj_qos_type pj_qos_flag - pj/types.h pj_status_t PJ_SUCCESS - """ - def __init__(self): - pass - - def process(self, listfile, outfile): - - # Read listfile - f = open(listfile) - lines = f.readlines() - f.close() - - # Process each line in list file, while generating the - # temporary C file to be processed by pycparser - f = open(TEMP_FILE, "w") - f.write(C_HEADING_SECTION) - names = [] - fcnt = 0 - for line in lines: - spec = line.split() - if len(spec) < 2: - continue - fcnt += 1 - f.write("#include <%s>\n" % spec[0]) - names.extend(spec[1:]) - f.close() - print 'Parsing %d symbols from %d files..' % (len(names), fcnt) - - # Parse the temporary C file - ast = pycparser.parse_file(TEMP_FILE, use_cpp=True, cpp_path=CPP_PATH, cpp_args=CPP_CFLAGS) - os.remove(TEMP_FILE) - - # Filter the declarations that we wanted - print 'Filtering..' - visitor = SymbolVisitor(names) - visitor.visit(ast) - - # Print symbol declarations to outfile - print 'Writing declarations..' - f = open(outfile, 'w') - f.write("// This file is autogenerated by importsym script, do not modify!\n\n") - gen = pycparser.c_generator.CGenerator() - for name in names: - node = visitor.nodeDict[name] - if not node: - print " warning: declaration for '%s' is not found **" % k - else: - print " writing '%s'.." % name - output = gen.visit(node) + ";\n\n" - f.write(output) - f.close() - print "Done." + """ + Import C selected declarations from C source file and move it + to another file. + + Parameters: + - listfile Path of file containing list of C source file + and identifier names to be imported. The format + of the listfile is: + + filename name1 name2 name3 + + for example: + + pj/sock_qos.h pj_qos_type pj_qos_flag + pj/types.h pj_status_t PJ_SUCCESS + """ + def __init__(self): + pass + + def process(self, listfile, outfile): + + # Read listfile + f = open(listfile) + lines = f.readlines() + f.close() + + # Process each line in list file, while generating the + # temporary C file to be processed by pycparser + f = open(TEMP_FILE, "w") + f.write(C_HEADING_SECTION) + names = [] + fcnt = 0 + for line in lines: + spec = line.split() + if len(spec) < 2: + continue + fcnt += 1 + f.write("#include <%s>\n" % spec[0]) + names.extend(spec[1:]) + f.close() + print 'Parsing %d symbols from %d files..' % (len(names), fcnt) + + try: + # Parse the temporary C file + ast = pycparser.parse_file(TEMP_FILE, use_cpp=True, cpp_path=CPP_PATH, cpp_args=CPP_CFLAGS) + os.remove(TEMP_FILE) + + # Filter the declarations that we wanted + print 'Filtering..' + visitor = SymbolVisitor(names) + visitor.visit(ast) + + # Print symbol declarations to outfile + print 'Writing declarations..' + f = open(outfile, 'w') + f.write("// This file is autogenerated by importsym script, do not modify!\n\n") + gen = pycparser.c_generator.CGenerator() + for name in names: + node = visitor.nodeDict[name] + if not node: + print " warning: declaration for '%s' is not found **" % k + else: + print " writing '%s'.." % name + output = gen.visit(node) + ";\n\n" + f.write(output) + f.close() + print "Done." + except Exception as e: + print e if __name__ == "__main__": - print "Importing symbols: 'symbols.lst' --> 'symbols.i'" - si = SymbolImporter() - si.process("symbols.lst", "symbols.i") - try: - os.remove("lextab.py") - except OSError: - pass - try: - os.remove("yacctab.py") - except OSError: - pass - - + print "Importing symbols: 'symbols.lst' --> 'symbols.i'" + si = SymbolImporter() + si.process("symbols.lst", "symbols.i") + try: + os.remove("lextab.py") + except OSError: + pass + try: + os.remove("yacctab.py") + except OSError: + pass diff --git a/pjsip-apps/src/swig/symbols.i b/pjsip-apps/src/swig/symbols.i index 5d49c4c06e..f441522d74 100644 --- a/pjsip-apps/src/swig/symbols.i +++ b/pjsip-apps/src/swig/symbols.i @@ -22,7 +22,8 @@ enum pj_file_access PJ_O_RDONLY = 0x1101, PJ_O_WRONLY = 0x1102, PJ_O_RDWR = 0x1103, - PJ_O_APPEND = 0x1108 + PJ_O_APPEND = 0x1108, + PJ_O_CLOEXEC = 0x1104 }; enum pj_log_decoration @@ -180,6 +181,7 @@ typedef enum pj_ssl_cert_verify_flag_t PJ_SSL_CERT_ECRL_FAILURE = 1 << 6, PJ_SSL_CERT_EREVOKED = 1 << 7, PJ_SSL_CERT_ECHAIN_TOO_LONG = 1 << 8, + PJ_SSL_CERT_EWEAK_SIGNATURE = 1 << 9, PJ_SSL_CERT_EIDENTITY_NOT_MATCH = 1 << 30, PJ_SSL_CERT_EUNKNOWN = 1 << 31 } pj_ssl_cert_verify_flag_t; @@ -437,10 +439,10 @@ typedef enum pjmedia_orient typedef enum pjmedia_frame_type { - PJMEDIA_FRAME_TYPE_NONE, - PJMEDIA_FRAME_TYPE_AUDIO, - PJMEDIA_FRAME_TYPE_EXTENDED, - PJMEDIA_FRAME_TYPE_VIDEO + PJMEDIA_FRAME_TYPE_NONE, + PJMEDIA_FRAME_TYPE_AUDIO, + PJMEDIA_FRAME_TYPE_EXTENDED, + PJMEDIA_FRAME_TYPE_VIDEO } pjmedia_frame_type; typedef enum pjmedia_format_id @@ -507,6 +509,17 @@ typedef enum pjsip_cred_data_type PJSIP_CRED_DATA_EXT_AKA = 16 } pjsip_cred_data_type; +typedef enum pjsip_auth_algorithm_type +{ + PJSIP_AUTH_ALGORITHM_NOT_SET = 0, + PJSIP_AUTH_ALGORITHM_MD5, + PJSIP_AUTH_ALGORITHM_SHA256, + PJSIP_AUTH_ALGORITHM_SHA512_256, + PJSIP_AUTH_ALGORITHM_AKAV1_MD5, + PJSIP_AUTH_ALGORITHM_AKAV2_MD5, + PJSIP_AUTH_ALGORITHM_COUNT +} pjsip_auth_algorithm_type; + typedef enum pjsip_dialog_cap_status { PJSIP_DIALOG_CAP_UNSUPPORTED = 0, diff --git a/pjsip-apps/src/swig/symbols.lst b/pjsip-apps/src/swig/symbols.lst index 3170fc5a1b..8c3237bc5a 100644 --- a/pjsip-apps/src/swig/symbols.lst +++ b/pjsip-apps/src/swig/symbols.lst @@ -1,8 +1,8 @@ -pj/types.h pj_status_t pj_constants_ pj_uint8_t pj_int32_t pj_uint32_t pj_uint16_t -pj/file_io.h pj_file_access -pj/log.h pj_log_decoration -pj/sock_qos.h pj_qos_type pj_qos_flag pj_qos_wmm_prio pj_qos_params -pj/ssl_sock.h pj_ssl_cipher pj_ssl_sock_proto pj_ssl_cert_name_type pj_ssl_cert_verify_flag_t pj_ssl_cert_lookup_type +pj/types.h pj_status_t pj_constants_ pj_uint8_t pj_int32_t pj_uint32_t pj_uint16_t +pj/file_io.h pj_file_access +pj/log.h pj_log_decoration +pj/sock_qos.h pj_qos_type pj_qos_flag pj_qos_wmm_prio pj_qos_params +pj/ssl_sock.h pj_ssl_cipher pj_ssl_sock_proto pj_ssl_cert_name_type pj_ssl_cert_verify_flag_t pj_ssl_cert_lookup_type pjnath/ice_session.h pj_ice_sess_trickle pjnath/nat_detect.h pj_stun_nat_type @@ -10,31 +10,31 @@ pjnath/turn_session.h pj_turn_tp_type pjmedia/echo.h pjmedia_echo_flag pjmedia/event.h pjmedia_event_type -pjmedia/transport_srtp.h pjmedia_srtp_use pjmedia_srtp_crypto_option pjmedia_srtp_keying_method -pjmedia/vid_stream.h pjmedia_vid_stream_rc_method -pjmedia-videodev/videodev.h pjmedia_vid_dev_index pjmedia_vid_dev_std_index pjmedia_vid_dev_cap pjmedia_vid_dev_fullscreen_flag +pjmedia/transport_srtp.h pjmedia_srtp_use pjmedia_srtp_crypto_option pjmedia_srtp_keying_method +pjmedia/vid_stream.h pjmedia_vid_stream_rc_method +pjmedia-videodev/videodev.h pjmedia_vid_dev_index pjmedia_vid_dev_std_index pjmedia_vid_dev_cap pjmedia_vid_dev_fullscreen_flag pjmedia-audiodev/audiodev.h pjmedia_aud_dev_route pjmedia_aud_dev_cap pjmedia/wav_port.h pjmedia_file_writer_option pjmedia_file_player_option -pjmedia/tonegen.h pjmedia_tone_digit pjmedia_tone_digit_map pjmedia_tone_desc +pjmedia/tonegen.h pjmedia_tone_digit pjmedia_tone_digit_map pjmedia_tone_desc pjmedia/types.h pjmedia_type pjmedia_dir pjmedia_tp_proto pjmedia_orient -pjmedia/frames.h pjmedia_frame_type -pjmedia/format.h pjmedia_format_id -pjmedia/vid_codec.h pjmedia_vid_packing -pjmedia/rtcp_fb.h pjmedia_rtcp_fb_type +pjmedia/frame.h pjmedia_frame_type +pjmedia/format.h pjmedia_format_id +pjmedia/vid_codec.h pjmedia_vid_packing +pjmedia/rtcp_fb.h pjmedia_rtcp_fb_type -pjsip/sip_auth.h pjsip_cred_data_type +pjsip/sip_auth.h pjsip_cred_data_type pjsip_auth_algorithm_type pjsip/sip_dialog.h pjsip_dialog_cap_status pjsip/sip_event.h pjsip_event_id_e -pjsip/sip_msg.h pjsip_status_code pjsip_hdr_e -pjsip/sip_transport.h pjsip_transport_type_e pjsip_transport_flags_e pjsip_transport_state -pjsip/sip_transport_tls.h pjsip_ssl_method +pjsip/sip_msg.h pjsip_status_code pjsip_hdr_e +pjsip/sip_transport.h pjsip_transport_type_e pjsip_transport_flags_e pjsip_transport_state +pjsip/sip_transport_tls.h pjsip_ssl_method pjsip/sip_transaction.h pjsip_tsx_state_e pjsip/sip_types.h pjsip_role_e pjsip/sip_util.h pjsip_redirect_op pjsip-simple/rpid.h pjrpid_activity -pjsip-simple/evsub.h pjsip_evsub_state +pjsip-simple/evsub.h pjsip_evsub_state pjsip-ua/sip_inv.h pjsip_inv_state -pjsua-lib/pjsua.h pjsua_invalid_id_const_ pjsua_state pjsua_stun_use pjsua_upnp_use pjsua_call_hold_type pjsua_acc_id pjsua_destroy_flag pjsua_100rel_use pjsua_sip_timer_use pjsua_ipv6_use pjsua_nat64_opt pjsua_buddy_status pjsua_call_media_status pjsua_vid_win_id pjsua_call_id pjsua_med_tp_st pjsua_call_vid_strm_op pjsua_vid_req_keyframe_method pjsua_call_flag pjsua_create_media_transport_flag pjsua_snd_dev_id pjsua_snd_dev_mode pjsua_ip_change_op pjsua_dtmf_method +pjsua-lib/pjsua.h pjsua_invalid_id_const_ pjsua_state pjsua_stun_use pjsua_upnp_use pjsua_call_hold_type pjsua_acc_id pjsua_destroy_flag pjsua_100rel_use pjsua_sip_timer_use pjsua_ipv6_use pjsua_nat64_opt pjsua_buddy_status pjsua_call_media_status pjsua_vid_win_id pjsua_call_id pjsua_med_tp_st pjsua_call_vid_strm_op pjsua_vid_req_keyframe_method pjsua_call_flag pjsua_create_media_transport_flag pjsua_snd_dev_id pjsua_snd_dev_mode pjsua_ip_change_op pjsua_dtmf_method diff --git a/pjsip/include/pjsua2/siptypes.hpp b/pjsip/include/pjsua2/siptypes.hpp index 1c1914564b..5192d5f37e 100644 --- a/pjsip/include/pjsua2/siptypes.hpp +++ b/pjsip/include/pjsua2/siptypes.hpp @@ -71,6 +71,12 @@ struct AuthCredInfo : public PersistentObject */ string data; + + /** + * Digest algorithm type. + */ + pjsip_auth_algorithm_type algoType; + /* * Digest AKA credential information. Note that when AKA credential * is being used, the \a data field of this pjsip_cred_info is diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c index b26166ac34..9951c6859f 100644 --- a/pjsip/src/pjsip/sip_auth_client.c +++ b/pjsip/src/pjsip/sip_auth_client.c @@ -736,7 +736,7 @@ static const pjsip_cred_info* auth_find_cred( const pjsip_auth_clt_sess *sess, case PJSIP_CRED_DATA_PLAIN_PASSWD: /* PLAIN_PASSWD creds can be used for any algorithm other than AKA */ if (algorithm_type != PJSIP_AUTH_ALGORITHM_AKAV1_MD5 - || algorithm_type != PJSIP_AUTH_ALGORITHM_AKAV2_MD5) { + && algorithm_type != PJSIP_AUTH_ALGORITHM_AKAV2_MD5) { break; } continue; diff --git a/pjsip/src/pjsua2/siptypes.cpp b/pjsip/src/pjsua2/siptypes.cpp index 6416964a46..081528e08a 100644 --- a/pjsip/src/pjsua2/siptypes.cpp +++ b/pjsip/src/pjsua2/siptypes.cpp @@ -151,6 +151,7 @@ void AuthCredInfo::fromPj(const pjsip_cred_info &prm) username = pj2Str(prm.username); dataType = prm.data_type; data = pj2Str(prm.data); + algoType = prm.algorithm_type; akaK = pj2Str(prm.ext.aka.k); akaOp = pj2Str(prm.ext.aka.op); akaAmf = pj2Str(prm.ext.aka.amf); @@ -159,11 +160,12 @@ void AuthCredInfo::fromPj(const pjsip_cred_info &prm) pjsip_cred_info AuthCredInfo::toPj() const { pjsip_cred_info ret; - ret.realm = str2Pj(realm); - ret.scheme = str2Pj(scheme); + ret.realm = str2Pj(realm); + ret.scheme = str2Pj(scheme); ret.username = str2Pj(username); ret.data_type = dataType; - ret.data = str2Pj(data); + ret.data = str2Pj(data); + ret.algorithm_type = algoType; ret.ext.aka.k = str2Pj(akaK); ret.ext.aka.op = str2Pj(akaOp); ret.ext.aka.amf = str2Pj(akaAmf); From 5259a2104691238f95509b6b53a0d25b14147d37 Mon Sep 17 00:00:00 2001 From: Amilcar Ubiera Date: Sun, 10 Nov 2024 21:18:23 -0500 Subject: [PATCH 096/491] Fix to compile error in on_make_call_med_tp_complete while accessing calls array with invalid call id. (#4144) --- pjsip/src/pjsua-lib/pjsua_call.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 2efa133eb5..fa3190b3dc 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -433,15 +433,21 @@ on_make_call_med_tp_complete(pjsua_call_id call_id, { pjmedia_sdp_session *offer = NULL; pjsip_inv_session *inv = NULL; - pjsua_call *call = &pjsua_var.calls[call_id]; - pjsua_acc *acc = &pjsua_var.acc[call->acc_id]; - pjsip_dialog *dlg = call->async_call.dlg; + pjsua_call *call; + pjsua_acc *acc; + pjsip_dialog *dlg; unsigned options = 0; pjsip_tx_data *tdata; pj_bool_t cb_called = PJ_FALSE; pjsip_tpselector tp_sel; pj_status_t status = (info? info->status: PJ_SUCCESS); + /* Get call, account, and dialog */ + PJ_ASSERT_RETURN(call_id != PJSUA_INVALID_ID, PJ_EINVAL); + call = &pjsua_var.calls[call_id]; + acc = &pjsua_var.acc[call->acc_id]; + dlg = call->async_call.dlg; + PJSUA_LOCK(); /* Increment the dialog's lock otherwise when invite session creation From e127571a2de6f277e62eacf86299a1c9d4b9653d Mon Sep 17 00:00:00 2001 From: sauwming Date: Mon, 11 Nov 2024 15:33:24 +0800 Subject: [PATCH 097/491] Misc Coverity fixes (#4145) --- pjlib-util/src/pjlib-util/crc32.c | 2 +- pjlib-util/src/pjlib-util/http_client.c | 2 +- pjlib/src/pj/atomic_queue.cpp | 4 +++- pjlib/src/pj/pool_caching.c | 2 ++ pjlib/src/pj/unittest.c | 6 ++++-- pjlib/src/pjlib-test/fifobuf.c | 1 - pjlib/src/pjlib-test/ssl_sock.c | 4 ++++ pjmedia/src/pjmedia/endpoint.c | 6 ++++-- pjmedia/src/pjmedia/stream.c | 2 +- pjsip/src/pjsip/sip_multipart.c | 3 ++- pjsip/src/pjsua-lib/pjsua_aud.c | 2 ++ 11 files changed, 24 insertions(+), 10 deletions(-) diff --git a/pjlib-util/src/pjlib-util/crc32.c b/pjlib-util/src/pjlib-util/crc32.c index a73f836d9b..4c968e8955 100644 --- a/pjlib-util/src/pjlib-util/crc32.c +++ b/pjlib-util/src/pjlib-util/crc32.c @@ -172,7 +172,7 @@ PJ_DEF(pj_uint32_t) pj_crc32_update(pj_crc32_context *ctx, data += 4; } - while (nbytes--) { + for (; nbytes; nbytes--) { crc = crc_tab[CRC32_INDEX(crc) ^ *data++] ^ CRC32_SHIFTED(crc); } diff --git a/pjlib-util/src/pjlib-util/http_client.c b/pjlib-util/src/pjlib-util/http_client.c index b6244dba35..dc3a4214d7 100644 --- a/pjlib-util/src/pjlib-util/http_client.c +++ b/pjlib-util/src/pjlib-util/http_client.c @@ -684,7 +684,7 @@ static pj_status_t http_response_parse(pj_pool_t *pool, pj_scan_fini(&scanner); /* Parse the response headers. */ - size = i - 2 - (end_status - newdata); + size = (i < 2)? 0: i - 2 - (end_status - newdata); if (size > 0) { status = http_headers_parse(end_status + 1, size, &response->headers); diff --git a/pjlib/src/pj/atomic_queue.cpp b/pjlib/src/pj/atomic_queue.cpp index f228b55a48..24ddb7620d 100644 --- a/pjlib/src/pj/atomic_queue.cpp +++ b/pjlib/src/pj/atomic_queue.cpp @@ -126,7 +126,9 @@ class AtomicQueue { return old_ptr; } - AtomicQueue():name_(""){} + AtomicQueue():maxItemCnt_(0), itemSize_(0), ptrWrite(0), + ptrRead(0), buffer(NULL), name_("") + {} }; struct pj_atomic_queue_t diff --git a/pjlib/src/pj/pool_caching.c b/pjlib/src/pj/pool_caching.c index 38511a764f..fd48badfb2 100644 --- a/pjlib/src/pj/pool_caching.c +++ b/pjlib/src/pj/pool_caching.c @@ -81,6 +81,8 @@ PJ_DEF(void) pj_caching_pool_init( pj_caching_pool *cp, */ //cp->factory.on_block_alloc = &cpool_on_block_alloc; //cp->factory.on_block_free = &cpool_on_block_free; + PJ_UNUSED_ARG(cpool_on_block_alloc); + PJ_UNUSED_ARG(cpool_on_block_free); pool = pj_pool_create_on_buf("cachingpool", cp->pool_buf, sizeof(cp->pool_buf)); status = pj_lock_create_simple_mutex(pool, "cachingpool", &cp->lock); diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index d8ac75132d..5b7cadede1 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -776,6 +776,7 @@ PJ_DEF(pj_status_t) pj_test_create_text_runner( text_runner_t *runner; unsigned i; pj_status_t status; + unsigned nthreads = (PJ_HAS_THREADS? 1: 0); *p_runner = NULL; @@ -795,13 +796,14 @@ PJ_DEF(pj_status_t) pj_test_create_text_runner( if (prm) { pj_memcpy(&runner->base.prm, prm, sizeof(*prm)); + nthreads = prm->nthreads; } else { pj_test_runner_param_default(&runner->base.prm); } runner->base.prm.nthreads = 0; - runner->threads = (pj_thread_t**) pj_pool_calloc(pool, prm->nthreads, + runner->threads = (pj_thread_t**) pj_pool_calloc(pool, nthreads, sizeof(pj_thread_t*)); - for (i=0; inthreads; ++i) { + for (i=0; irunner = runner; tprm->tid = i+1; diff --git a/pjlib/src/pjlib-test/fifobuf.c b/pjlib/src/pjlib-test/fifobuf.c index 1d42bf936c..684d0ecc60 100644 --- a/pjlib/src/pjlib-test/fifobuf.c +++ b/pjlib/src/pjlib-test/fifobuf.c @@ -133,7 +133,6 @@ static int fifobuf_misc_test() LOOP=10000 }; pj_pool_t *pool; pj_fifobuf_t fifo; - pj_size_t available = SIZE; void *entries[MAX_ENTRIES]; void *buffer; int i; diff --git a/pjlib/src/pjlib-test/ssl_sock.c b/pjlib/src/pjlib-test/ssl_sock.c index a4e8570fea..a6d1fae0e4 100644 --- a/pjlib/src/pjlib-test/ssl_sock.c +++ b/pjlib/src/pjlib-test/ssl_sock.c @@ -540,6 +540,8 @@ static pj_status_t load_cert_to_buf(pj_pool_t *pool, const pj_str_t *file_name, } #endif +#if (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_SCHANNEL) + static pj_status_t load_cert_from_store(pj_pool_t *pool, pj_ssl_cert_t **p_cert) { @@ -573,6 +575,8 @@ static pj_status_t load_cert_from_store(pj_pool_t *pool, #endif } +#endif + static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto, pj_ssl_cipher srv_cipher, pj_ssl_cipher cli_cipher, pj_bool_t req_client_cert, pj_bool_t client_provide_cert) diff --git a/pjmedia/src/pjmedia/endpoint.c b/pjmedia/src/pjmedia/endpoint.c index b9fe6b642b..62f3a7b42d 100644 --- a/pjmedia/src/pjmedia/endpoint.c +++ b/pjmedia/src/pjmedia/endpoint.c @@ -486,8 +486,10 @@ pjmedia_endpt_create_audio_sdp(pjmedia_endpt *endpt, break; codec_info = &endpt->codec_mgr.codec_desc[i].info; - pjmedia_codec_mgr_get_default_param(&endpt->codec_mgr, codec_info, - &codec_param); + status = pjmedia_codec_mgr_get_default_param(&endpt->codec_mgr, + codec_info, &codec_param); + if (status != PJ_SUCCESS) + return status; fmt = &m->desc.fmt[m->desc.fmt_count++]; pt = codec_info->pt; diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index bea1e717d9..d76df23523 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -996,7 +996,7 @@ static void create_dtmf_payload(pjmedia_stream *stream, } #endif if (!pj_stricmp2(&stream->si.fmt.encoding_name, "opus")) { - ts_modifier = 48000 / stream->codec_param.info.clock_rate; + ts_modifier = (float)48000 / stream->codec_param.info.clock_rate; } duration = ts_modifier * digit->send_duration * stream->codec_param.info.clock_rate / 1000; } diff --git a/pjsip/src/pjsip/sip_multipart.c b/pjsip/src/pjsip/sip_multipart.c index e09167643d..a6b66e2fee 100644 --- a/pjsip/src/pjsip/sip_multipart.c +++ b/pjsip/src/pjsip/sip_multipart.c @@ -635,7 +635,7 @@ static pjsip_multipart_part *parse_multipart_part(pj_pool_t *pool, const pjsip_media_type *pct) { pjsip_multipart_part *part = pjsip_multipart_create_part(pool); - char *p = start, *end = start+len, *end_hdr = NULL, *start_body = NULL; + char *p = start, *end = start+len, *end_hdr = NULL, *start_body; pjsip_ctype_hdr *ctype_hdr = NULL; TRACE_((THIS_FILE, "Parsing part: begin--\n%.*s\n--end", @@ -657,6 +657,7 @@ static pjsip_multipart_part *parse_multipart_part(pj_pool_t *pool, /* Empty body section */ end_hdr = end; start_body = ++p; + break; } else if ((p>=start+1 && *(p-1)=='\n') || (p>=start+2 && *(p-1)=='\r' && *(p-2)=='\n')) { diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index 0f8a756f5b..2137eff2ba 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -1260,6 +1260,8 @@ PJ_DEF(pj_status_t) pjsua_player_create( const pj_str_t *filename, pjmedia_port *port; pj_status_t status = PJ_SUCCESS; + PJ_ASSERT_RETURN(filename && filename->slen > 0, PJ_EINVAL); + if (pjsua_var.player_cnt >= PJ_ARRAY_SIZE(pjsua_var.player)) return PJ_ETOOMANY; From d0df95a433427f914c1cd55319b521d9e4ce7726 Mon Sep 17 00:00:00 2001 From: sauwming Date: Tue, 12 Nov 2024 11:09:15 +0800 Subject: [PATCH 098/491] Misc fix (Coverity and build warning) (#4146) --- pjlib/src/pj/ssl_sock_apple.m | 7 ++++++- pjsip/src/pjsua2/siptypes.cpp | 6 ++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/pjlib/src/pj/ssl_sock_apple.m b/pjlib/src/pj/ssl_sock_apple.m index d2660f825e..795aba6cc0 100644 --- a/pjlib/src/pj/ssl_sock_apple.m +++ b/pjlib/src/pj/ssl_sock_apple.m @@ -576,7 +576,7 @@ static pj_status_t create_identity_from_cert(applessl_sock_t *assock, identity = (SecIdentityRef) CFDictionaryGetValue((CFDictionaryRef) item, kSecImportItemIdentity); - identity = CFRetain(identity); + CFRetain(identity); break; } #if !TARGET_OS_IPHONE @@ -1416,6 +1416,11 @@ static pj_status_t ssl_create(pj_ssl_sock_t *ssock) * is started. */ PJ_UNUSED_ARG(ssock); + + /* Suppress warnings */ + PJ_UNUSED_ARG(circ_reset); + PJ_UNUSED_ARG(circ_read_cancel); + return PJ_SUCCESS; } diff --git a/pjsip/src/pjsua2/siptypes.cpp b/pjsip/src/pjsua2/siptypes.cpp index 081528e08a..db9ac9d5d7 100644 --- a/pjsip/src/pjsua2/siptypes.cpp +++ b/pjsip/src/pjsua2/siptypes.cpp @@ -102,7 +102,8 @@ void writeSipHeaders(ContainerNode &node, /////////////////////////////////////////////////////////////////////////////// AuthCredInfo::AuthCredInfo() -: scheme("digest"), realm("*"), dataType(0) +: scheme("digest"), realm("*"), dataType(0), + algoType(PJSIP_AUTH_ALGORITHM_NOT_SET) { } @@ -112,7 +113,8 @@ AuthCredInfo::AuthCredInfo(const string ¶m_scheme, const int param_data_type, const string param_data) : scheme(param_scheme), realm(param_realm), username(param_user_name), - dataType(param_data_type), data(param_data) + dataType(param_data_type), data(param_data), + algoType(PJSIP_AUTH_ALGORITHM_NOT_SET) { } From 0946631bdd92a0a290980e53653ab4bd1f92edd4 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 12 Nov 2024 13:07:01 +0700 Subject: [PATCH 099/491] Fix sound device idle timer after conference bridge changed to async (#4148) --- pjsip/src/pjsua-lib/pjsua_aud.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index 2137eff2ba..a4f95e21b7 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -479,10 +479,11 @@ void pjsua_check_snd_dev_idle() /* Activate sound device auto-close timer if sound device is idle. * It is idle when there is no port connection in the bridge and * there is no active call. + * Update: as bridge conn/disconn is now async, the check is moved to + * the timer callback. */ if (pjsua_var.snd_idle_timer.id == PJ_FALSE && - call_cnt == 0 && - pjmedia_conf_get_connect_count(pjsua_var.mconf) == 0) + call_cnt == 0) { pj_time_val delay; @@ -502,14 +503,13 @@ static void close_snd_timer_cb( pj_timer_heap_t *th, PJ_UNUSED_ARG(th); PJSUA_LOCK(); - if (entry->id) { + if (entry->id && pjmedia_conf_get_connect_count(pjsua_var.mconf) == 0) { PJ_LOG(4,(THIS_FILE,"Closing sound device after idle for %d second(s)", pjsua_var.media_cfg.snd_auto_close_time)); - entry->id = PJ_FALSE; - close_snd_dev(); } + entry->id = PJ_FALSE; PJSUA_UNLOCK(); } From c93c7c109b785b522aeed7402a526742fd96a145 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 12 Nov 2024 14:43:28 +0700 Subject: [PATCH 100/491] Add software clock to sound port (#4149) --- pjlib/include/pj/config_site_sample.h | 7 +- pjmedia/include/pjmedia/sound_port.h | 18 ++- pjmedia/src/pjmedia/sound_port.c | 184 ++++++++++++++++++++++---- pjsip/include/pjsua-lib/pjsua.h | 19 +++ pjsip/include/pjsua2/endpoint.hpp | 10 ++ pjsip/src/pjsua-lib/pjsua_aud.c | 2 + pjsip/src/pjsua-lib/pjsua_core.c | 1 + pjsip/src/pjsua2/endpoint.cpp | 4 + 8 files changed, 216 insertions(+), 29 deletions(-) diff --git a/pjlib/include/pj/config_site_sample.h b/pjlib/include/pj/config_site_sample.h index fb68d4d5d1..f188c0be6d 100644 --- a/pjlib/include/pj/config_site_sample.h +++ b/pjlib/include/pj/config_site_sample.h @@ -389,7 +389,12 @@ /* Fine tune Speex's default settings for best performance/quality */ #define PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY 5 - + + /* Using software clock for media flow can improve quality, + * i.e: more consistent RTP timing and less jitter/burst. + */ + #define PJSUA_DEFAULT_SND_USE_SW_CLOCK PJ_TRUE + /* * PJSIP settings. */ diff --git a/pjmedia/include/pjmedia/sound_port.h b/pjmedia/include/pjmedia/sound_port.h index a5af4cafb3..a448a514b9 100644 --- a/pjmedia/include/pjmedia/sound_port.h +++ b/pjmedia/include/pjmedia/sound_port.h @@ -71,7 +71,15 @@ enum pjmedia_snd_port_option /** * Don't start the audio device when creating a sound port. */ - PJMEDIA_SND_PORT_NO_AUTO_START = 1 + PJMEDIA_SND_PORT_NO_AUTO_START = 1, + + /** + * Sound device uses \ref PJMEDIA_CLOCK instead of native sound device + * clock, generally this will be able to reduce jitter and clock drift. + * + * This option is not applicable for encoded/non-PCM format. + */ + PJMEDIA_SND_PORT_USE_SW_CLOCK = 2 }; /** @@ -87,7 +95,7 @@ typedef struct pjmedia_snd_port_param pjmedia_aud_param base; /** - * Sound port creation options. + * Sound port creation options (see #pjmedia_snd_port_option). */ unsigned options; @@ -161,7 +169,7 @@ typedef struct pjmedia_snd_port pjmedia_snd_port; * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Set the number of bits per sample. The normal * value for this parameter is 16 bits per sample. - * @param options Options flag. + * @param options Options flag (see #pjmedia_snd_port_option). * @param p_port Pointer to receive the sound device port instance. * * @return PJ_SUCCESS on success, or the appropriate error @@ -191,7 +199,7 @@ PJ_DECL(pj_status_t) pjmedia_snd_port_create( pj_pool_t *pool, * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Set the number of bits per sample. The normal * value for this parameter is 16 bits per sample. - * @param options Options flag. + * @param options Options flag (see #pjmedia_snd_port_option). * @param p_port Pointer to receive the sound device port instance. * * @return PJ_SUCCESS on success, or the appropriate error @@ -220,7 +228,7 @@ PJ_DECL(pj_status_t) pjmedia_snd_port_create_rec(pj_pool_t *pool, * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Set the number of bits per sample. The normal * value for this parameter is 16 bits per sample. - * @param options Options flag. + * @param options Options flag (see #pjmedia_snd_port_option). * @param p_port Pointer to receive the sound device port instance. * * @return PJ_SUCCESS on success, or the appropriate error diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c index e459319f3a..fc514e665e 100644 --- a/pjmedia/src/pjmedia/sound_port.c +++ b/pjmedia/src/pjmedia/sound_port.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,12 @@ struct pjmedia_snd_port pjmedia_dir dir; pjmedia_port *port; + /* Use clock */ + pjmedia_clock *clock; + pjmedia_delay_buf *play_dbuf; + pjmedia_delay_buf *cap_dbuf; + pj_int16_t *frame_buf; + pjmedia_clock_src cap_clocksrc, play_clocksrc; @@ -78,18 +85,25 @@ static pj_status_t play_cb(void *user_data, pjmedia_frame *frame) const unsigned required_size = (unsigned)frame->size; pj_status_t status; - pjmedia_clock_src_update(&snd_port->play_clocksrc, &frame->timestamp); - port = snd_port->port; if (port == NULL) goto no_frame; - status = pjmedia_port_get_frame(port, frame); - if (status != PJ_SUCCESS) - goto no_frame; + if (snd_port->options & PJMEDIA_SND_PORT_USE_SW_CLOCK) { + pj_assert(frame->size >= snd_port->samples_per_frame*2); + status = pjmedia_delay_buf_get(snd_port->play_dbuf, frame->buf); + if (status != PJ_SUCCESS) + goto no_frame; + } else { + pjmedia_clock_src_update(&snd_port->play_clocksrc, &frame->timestamp); - if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) - goto no_frame; + status = pjmedia_port_get_frame(port, frame); + if (status != PJ_SUCCESS) + goto no_frame; + + if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) + goto no_frame; + } /* Must supply the required samples */ pj_assert(frame->size == required_size); @@ -144,23 +158,25 @@ static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame) pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data; pjmedia_port *port; - pjmedia_clock_src_update(&snd_port->cap_clocksrc, &frame->timestamp); - /* Invoke preview callback */ if (snd_port->on_rec_frame) (*snd_port->on_rec_frame)(snd_port->user_data, frame); - port = snd_port->port; - if (port == NULL) - return PJ_SUCCESS; - /* Cancel echo */ if (snd_port->ec_state && !snd_port->ec_suspended) { pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) frame->buf, 0); } - pjmedia_port_put_frame(port, frame); + port = snd_port->port; + if (port == NULL) + return PJ_SUCCESS; + if (snd_port->options & PJMEDIA_SND_PORT_USE_SW_CLOCK) { + pjmedia_delay_buf_put(snd_port->cap_dbuf, frame->buf); + } else { + pjmedia_clock_src_update(&snd_port->cap_clocksrc, &frame->timestamp); + pjmedia_port_put_frame(port, frame); + } return PJ_SUCCESS; } @@ -216,6 +232,51 @@ static pj_status_t rec_cb_ext(void *user_data, pjmedia_frame *frame) return PJ_SUCCESS; } + +/* + * Callback to be called for each clock ticks. + */ +static void clock_callback(const pj_timestamp *ts, void *user_data) +{ + pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data; + pjmedia_port *port = snd_port->port; + pjmedia_frame frame; + pj_status_t status; + + /* Sound port not connected */ + if (port == NULL) + return; + + /* Playback, get a frame from underlying port, put into delay buffer */ + pjmedia_clock_src_update(&snd_port->play_clocksrc, ts); + + pj_bzero(&frame, sizeof(frame)); + frame.type = PJMEDIA_FRAME_TYPE_AUDIO; + frame.buf = snd_port->frame_buf; + frame.size = snd_port->samples_per_frame * 2; + frame.timestamp.u64 = ts->u64; + status = pjmedia_port_get_frame(port, &frame); + if (status != PJ_SUCCESS || frame.type != PJMEDIA_FRAME_TYPE_AUDIO) { + pjmedia_zero_samples(snd_port->frame_buf, snd_port->samples_per_frame); + } + pjmedia_delay_buf_put(snd_port->play_dbuf, frame.buf); + + /* Record: get a frame from delay buffer, put into underlying port */ + pjmedia_clock_src_update(&snd_port->cap_clocksrc, ts); + + status = pjmedia_delay_buf_get(snd_port->cap_dbuf, snd_port->frame_buf); + pj_bzero(&frame, sizeof(frame)); + frame.type = PJMEDIA_FRAME_TYPE_AUDIO; + frame.buf = snd_port->frame_buf; + frame.size = snd_port->samples_per_frame * 2; + frame.timestamp.u64 = ts->u64; + if (status != PJ_SUCCESS) { + pjmedia_zero_samples(snd_port->frame_buf, snd_port->samples_per_frame); + } + pjmedia_port_put_frame(port, &frame); +} + + /* Initialize with default values (zero) */ PJ_DEF(void) pjmedia_snd_port_param_default(pjmedia_snd_port_param *prm) { @@ -322,24 +383,79 @@ static pj_status_t start_sound_device( pj_pool_t *pool, status = pjmedia_snd_port_set_ec(snd_port, pool, snd_port->aud_param.ec_tail_ms, snd_port->prm_ec_options); - if (status != PJ_SUCCESS) { - pjmedia_aud_stream_destroy(snd_port->aud_stream); - snd_port->aud_stream = NULL; - return status; + if (status != PJ_SUCCESS) + goto on_error; + } + + /* Create clock and buffers, if configured */ + if ((snd_port->options & PJMEDIA_SND_PORT_USE_SW_CLOCK) && + (snd_port->aud_param.ext_fmt.id == PJMEDIA_FORMAT_L16)) + { + unsigned ptime; + + status = pjmedia_clock_create(pool, + snd_port->clock_rate, + snd_port->channel_count, + snd_port->samples_per_frame, + 0, + &clock_callback, + snd_port, + &snd_port->clock); + if (status != PJ_SUCCESS) + goto on_error; + + ptime = snd_port->samples_per_frame * 1000 / snd_port->clock_rate / + snd_port->channel_count; + status = pjmedia_delay_buf_create(pool, "playdbuf", + snd_port->clock_rate, + snd_port->samples_per_frame, + snd_port->channel_count, + PJMEDIA_SOUND_BUFFER_COUNT * ptime, + 0, /* options */ + &snd_port->play_dbuf); + if (status != PJ_SUCCESS) + goto on_error; + + status = pjmedia_delay_buf_create(pool, "capdbuf", + snd_port->clock_rate, + snd_port->samples_per_frame, + snd_port->channel_count, + PJMEDIA_SOUND_BUFFER_COUNT * ptime, + 0, /* options */ + &snd_port->cap_dbuf); + if (status != PJ_SUCCESS) + goto on_error; + + snd_port->frame_buf = (pj_int16_t*) + pj_pool_zalloc(pool, + snd_port->samples_per_frame * 2); + if (!snd_port->frame_buf) { + status = PJ_ENOMEM; + goto on_error; } + + status = pjmedia_clock_start(snd_port->clock); + if (status != PJ_SUCCESS) + goto on_error; + + PJ_LOG(4,(THIS_FILE, "Sound port uses internal (or software) clock")); + } else { + PJ_LOG(4,(THIS_FILE, "Sound port uses native clock")); } /* Start sound stream. */ if (!(snd_port->options & PJMEDIA_SND_PORT_NO_AUTO_START)) { status = pjmedia_aud_stream_start(snd_port->aud_stream); } - if (status != PJ_SUCCESS) { - pjmedia_aud_stream_destroy(snd_port->aud_stream); - snd_port->aud_stream = NULL; - return status; - } + if (status != PJ_SUCCESS) + goto on_error; return PJ_SUCCESS; + +on_error: + pjmedia_aud_stream_destroy(snd_port->aud_stream); + snd_port->aud_stream = NULL; + return status; } @@ -349,6 +465,13 @@ static pj_status_t start_sound_device( pj_pool_t *pool, */ static pj_status_t stop_sound_device( pjmedia_snd_port *snd_port ) { + /* Stop and destroy clock */ + if (snd_port->clock) { + pjmedia_clock_stop(snd_port->clock); + pjmedia_clock_destroy(snd_port->clock); + snd_port->clock = NULL; + } + /* Check if we have sound stream device. */ if (snd_port->aud_stream) { pjmedia_aud_stream_stop(snd_port->aud_stream); @@ -356,6 +479,16 @@ static pj_status_t stop_sound_device( pjmedia_snd_port *snd_port ) snd_port->aud_stream = NULL; } + /* Destroy buffers for clock */ + if (snd_port->play_dbuf) { + pjmedia_delay_buf_destroy(snd_port->play_dbuf); + snd_port->play_dbuf = NULL; + } + if (snd_port->cap_dbuf) { + pjmedia_delay_buf_destroy(snd_port->cap_dbuf); + snd_port->cap_dbuf = NULL; + } + /* Destroy AEC */ if (snd_port->ec_state) { pjmedia_echo_destroy(snd_port->ec_state); @@ -774,6 +907,11 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_connect( pjmedia_snd_port *snd_port, if (afd->bits_per_sample != snd_port->bits_per_sample) return PJMEDIA_ENCBITS; + if (snd_port->play_dbuf) + pjmedia_delay_buf_reset(snd_port->play_dbuf); + if (snd_port->cap_dbuf) + pjmedia_delay_buf_reset(snd_port->cap_dbuf); + /* Port is okay. */ snd_port->port = port; return PJ_SUCCESS; diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 7245a6ccc6..6014c957d4 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -7138,6 +7138,15 @@ PJ_DECL(pj_status_t) pjsua_im_typing(pjsua_acc_id acc_id, # define PJSUA_DEFAULT_CLOCK_RATE 16000 #endif +/** + * Sound device uses \ref PJMEDIA_CLOCK instead of native sound device + * clock. This setting is the default value for + * pjsua_media_config.snd_use_sw_clock. + */ +#ifndef PJSUA_DEFAULT_SND_USE_SW_CLOCK +# define PJSUA_DEFAULT_SND_USE_SW_CLOCK PJ_FALSE +#endif + /** * Default frame length in the conference bridge. This setting * is the default value for pjsua_media_config.audio_frame_ptime. @@ -7225,6 +7234,16 @@ struct pjsua_media_config */ unsigned snd_clock_rate; + /** + * Sound device uses \ref PJMEDIA_CLOCK instead of native sound device + * clock, generally this will be able to reduce jitter and clock drift. + * + * This option is not applicable for encoded/non-PCM format. + * + * Default value: PJSUA_DEFAULT_SND_USE_SW_CLOCK + */ + pj_bool_t snd_use_sw_clock; + /** * Channel count be applied when opening the sound device and * conference bridge. diff --git a/pjsip/include/pjsua2/endpoint.hpp b/pjsip/include/pjsua2/endpoint.hpp index 133159267d..774edb759d 100644 --- a/pjsip/include/pjsua2/endpoint.hpp +++ b/pjsip/include/pjsua2/endpoint.hpp @@ -944,6 +944,16 @@ struct MediaConfig : public PersistentObject */ unsigned sndClockRate; + /** + * Sound device uses \ref PJMEDIA_CLOCK instead of native sound device + * clock, generally this will be able to reduce jitter and clock drift. + * + * This option is not applicable for encoded/non-PCM format. + * + * Default value: PJSUA_DEFAULT_SND_USE_SW_CLOCK + */ + bool sndUseSwClock; + /** * Channel count be applied when opening the sound device and * conference bridge. diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index a4f95e21b7..9991ad1053 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -2358,6 +2358,8 @@ PJ_DEF(pj_status_t) pjsua_set_snd_dev2(const pjsua_snd_dev_param *snd_param) /* Open! */ param.options = 0; + if (pjsua_var.media_cfg.snd_use_sw_clock) + param.options |= PJMEDIA_SND_PORT_USE_SW_CLOCK; status = open_snd_dev(¶m); if (status == PJ_SUCCESS) break; diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 41aacda03f..cc6976adbc 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -411,6 +411,7 @@ PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg) } else { cfg->snd_clock_rate = 0; } + cfg->snd_use_sw_clock = PJSUA_DEFAULT_SND_USE_SW_CLOCK; cfg->channel_count = 1; cfg->audio_frame_ptime = PJSUA_DEFAULT_AUDIO_FRAME_PTIME; cfg->max_media_ports = PJSUA_MAX_CONF_PORTS; diff --git a/pjsip/src/pjsua2/endpoint.cpp b/pjsip/src/pjsua2/endpoint.cpp index 829f70a300..610ec8285b 100644 --- a/pjsip/src/pjsua2/endpoint.cpp +++ b/pjsip/src/pjsua2/endpoint.cpp @@ -433,6 +433,7 @@ void MediaConfig::fromPj(const pjsua_media_config &mc) { this->clockRate = mc.clock_rate; this->sndClockRate = mc.snd_clock_rate; + this->sndUseSwClock = PJ2BOOL(mc.snd_use_sw_clock); this->channelCount = mc.channel_count; this->audioFramePtime = mc.audio_frame_ptime; this->maxMediaPorts = mc.max_media_ports; @@ -465,6 +466,7 @@ pjsua_media_config MediaConfig::toPj() const mcfg.clock_rate = this->clockRate; mcfg.snd_clock_rate = this->sndClockRate; + mcfg.snd_use_sw_clock = this->sndUseSwClock; mcfg.channel_count = this->channelCount; mcfg.audio_frame_ptime = this->audioFramePtime; mcfg.max_media_ports = this->maxMediaPorts; @@ -519,6 +521,7 @@ void MediaConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error) NODE_READ_NUM_T ( this_node, pjmedia_jb_discard_algo, jbDiscardAlgo); NODE_READ_INT ( this_node, sndAutoCloseTime); NODE_READ_BOOL ( this_node, vidPreviewEnableNative); + NODE_READ_BOOL ( this_node, sndUseSwClock); } void MediaConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error) @@ -549,6 +552,7 @@ void MediaConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error) NODE_WRITE_NUM_T ( this_node, pjmedia_jb_discard_algo, jbDiscardAlgo); NODE_WRITE_INT ( this_node, sndAutoCloseTime); NODE_WRITE_BOOL ( this_node, vidPreviewEnableNative); + NODE_WRITE_BOOL ( this_node, sndUseSwClock); } /////////////////////////////////////////////////////////////////////////////// From b4340b5827209a5ec3549787528b7ab6eca69414 Mon Sep 17 00:00:00 2001 From: sauwming Date: Tue, 12 Nov 2024 17:34:16 +0800 Subject: [PATCH 101/491] Modified dialog response status code upon receiving bad request (#4147) --- pjsip/src/pjsip/sip_dialog.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c index d83cd0d3c7..5bb81b820d 100644 --- a/pjsip/src/pjsip/sip_dialog.c +++ b/pjsip/src/pjsip/sip_dialog.c @@ -621,7 +621,13 @@ pj_status_t create_uas_dialog( pjsip_user_agent *ua, } if (tsx) { - pjsip_tsx_terminate(tsx, 500); + int st_code; + + st_code = (status == PJSIP_ENOTREQUESTMSG || + status == PJSIP_EMISSINGHDR || + status == PJSIP_EINVALIDHDR)? PJSIP_SC_BAD_REQUEST: + PJSIP_SC_INTERNAL_SERVER_ERROR; + pjsip_tsx_terminate(tsx, st_code); pj_assert(dlg->tsx_count>0); --dlg->tsx_count; } @@ -1789,9 +1795,14 @@ void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata ) */ char errmsg[PJ_ERR_MSG_SIZE]; pj_str_t reason; + int st_code; + st_code = (status == PJSIP_ENOTREQUESTMSG || + status == PJSIP_EMISSINGHDR || + status == PJSIP_EINVALIDHDR)? PJSIP_SC_BAD_REQUEST: + PJSIP_SC_INTERNAL_SERVER_ERROR; reason = pj_strerror(status, errmsg, sizeof(errmsg)); - pjsip_endpt_respond_stateless(dlg->endpt, rdata, 500, &reason, + pjsip_endpt_respond_stateless(dlg->endpt, rdata, st_code, &reason, NULL, NULL); goto on_return; } From 22464d7a052ab5b7775496bb52211ea66dc2d62d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=9B=D1=83=D1=85?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2?= Date: Wed, 13 Nov 2024 10:26:34 +0300 Subject: [PATCH 102/491] Expand tab to spaces and and cleanup formatting This is a followup for https://github.com/pjsip/pjproject/pull/3292 and https://github.com/pjsip/pjproject/pull/3210 but regarding aconfigure.ac --- .editorconfig | 2 +- aconfigure | 3180 ++++++++++++++++++++----------------- aconfigure.ac | 4133 ++++++++++++++++++++++++++----------------------- 3 files changed, 3922 insertions(+), 3393 deletions(-) diff --git a/.editorconfig b/.editorconfig index 1b42aeabd7..5bea015dce 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,7 @@ root = true # properties -[*.{h,c,hpp,cpp,m,java}] +[*.{h,c,hpp,cpp,m,java,ac}] insert_final_newline = true indent_style = space indent_size = 4 diff --git a/aconfigure b/aconfigure index 47fce480ee..cead0631dc 100755 --- a/aconfigure +++ b/aconfigure @@ -1567,16 +1567,12 @@ Optional Features: Exclude Android MediaCodec (default: autodetect) --disable-darwin-ssl Exclude Darwin SSL (default: autodetect) --disable-ssl Exclude SSL support the build (default: autodetect) - --disable-opencore-amr Exclude OpenCORE AMR support from the build (default: autodetect) - --disable-silk Exclude SILK support from the build (default: autodetect) - --disable-opus Exclude OPUS support from the build (default: autodetect) - --disable-bcg729 Disable bcg729 (default: not disabled) --disable-lyra Disable lyra (default: not disabled) --disable-libsrtp Exclude libsrtp in the build @@ -3222,12 +3218,12 @@ test -n "$target_alias" && program_prefix=${target_alias}- ac_config_headers="$ac_config_headers pjlib/include/pj/compat/os_auto.h pjlib/include/pj/compat/m_auto.h pjmedia/include/pjmedia/config_auto.h pjmedia/include/pjmedia-codec/config_auto.h pjsip/include/pjsip/sip_autoconf.h" -ac_config_files="$ac_config_files build.mak build/os-auto.mak build/cc-auto.mak pjlib/build/os-auto.mak pjlib-util/build/os-auto.mak pjmedia/build/os-auto.mak pjsip/build/os-auto.mak third_party/build/os-auto.mak" +ac_config_files="$ac_config_files build.mak build/os-auto.mak build/cc-auto.mak pjlib/build/os-auto.mak pjlib-util/build/os-auto.mak pjmedia/build/os-auto.mak pjsip/build/os-auto.mak third_party/build/os-auto.mak" if test "$CFLAGS" = ""; then - CFLAGS="-O2" + CFLAGS="-O2" fi @@ -4505,11 +4501,11 @@ if test x$ac_prog_cxx_stdcxx = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++11 features" >&5 printf %s "checking for $CXX option to enable C++11 features... " >&6; } -if test ${ac_cv_prog_cxx_11+y} +if test ${ac_cv_prog_cxx_cxx11+y} then : printf %s "(cached) " >&6 else $as_nop - ac_cv_prog_cxx_11=no + ac_cv_prog_cxx_cxx11=no ac_save_CXX=$CXX cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -4551,11 +4547,11 @@ if test x$ac_prog_cxx_stdcxx = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++98 features" >&5 printf %s "checking for $CXX option to enable C++98 features... " >&6; } -if test ${ac_cv_prog_cxx_98+y} +if test ${ac_cv_prog_cxx_cxx98+y} then : printf %s "(cached) " >&6 else $as_nop - ac_cv_prog_cxx_98=no + ac_cv_prog_cxx_cxx98=no ac_save_CXX=$CXX cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -4882,29 +4878,29 @@ if test "$CC_CFLAGS" = ""; then CC_CFLAGS="-Wall"; fi case $host in *mingw* | *cygw* | *win32* | *w32* ) - if pwd -W 2&> /dev/null; then - ac_pjdir=`pwd -W` - else - # We're probably cross-compiling mingw on Linux - ac_pjdir=`pwd` - fi - ;; + if pwd -W 2&> /dev/null; then + ac_pjdir=`pwd -W` + else + # We're probably cross-compiling mingw on Linux + ac_pjdir=`pwd` + fi + ;; *) - ac_pjdir=`pwd` - ;; + ac_pjdir=`pwd` + ;; esac case $target in *mingw* | *cygw* | *win32* | *w32* ) - ac_shlib_suffix=dll - ;; + ac_shlib_suffix=dll + ;; *darwin*) - ac_shlib_suffix=dylib - ;; + ac_shlib_suffix=dylib + ;; *) - ac_shlib_suffix=so - ;; + ac_shlib_suffix=so + ;; esac @@ -5263,14 +5259,16 @@ fi # Check whether --enable-libuuid was given. if test ${enable_libuuid+y} then : - enableval=$enable_libuuid; if test "$enable_libuuid" = "no"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libuuid disabled... yes" >&5 + enableval=$enable_libuuid; + if test "$enable_libuuid" = "no"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libuuid disabled... yes" >&5 printf "%s\n" "Checking if libuuid disabled... yes" >&6; } - ac_has_uuid_lib=0 - fi + ac_has_uuid_lib=0 + fi + else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uuid_generate in -luuid" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uuid_generate in -luuid" >&5 printf %s "checking for uuid_generate in -luuid... " >&6; } if test ${ac_cv_lib_uuid_uuid_generate+y} then : @@ -5313,7 +5311,7 @@ then : fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uuid_generate in -luuid" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uuid_generate in -luuid" >&5 printf %s "checking for uuid_generate in -luuid... " >&6; } if test ${ac_cv_lib_uuid_uuid_generate+y} then : @@ -5354,6 +5352,7 @@ then : fi + fi @@ -5426,17 +5425,17 @@ printf "%s\n" "#define PJ_M_NAME \"$target_cpu\"" >>confdefs.h printf %s "checking memory alignment... " >&6; } case $target in sparc64-* | ia64-* | x86_64-* | arm64-* | aarch64-* | mips64* ) - printf "%s\n" "#define PJ_POOL_ALIGNMENT 8" >>confdefs.h + printf "%s\n" "#define PJ_POOL_ALIGNMENT 8" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: 8 bytes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: 8 bytes" >&5 printf "%s\n" "8 bytes" >&6; } - ;; + ;; * ) - printf "%s\n" "#define PJ_POOL_ALIGNMENT 4" >>confdefs.h + printf "%s\n" "#define PJ_POOL_ALIGNMENT 4" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: 4 bytes (default)" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: 4 bytes (default)" >&5 printf "%s\n" "4 bytes (default)" >&6; } - ;; + ;; esac @@ -5704,77 +5703,80 @@ printf "%s\n" "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h if test "x$ac_cv_c_bigendian" = "xyes"; then - CFLAGS="$CFLAGS -DPJ_IS_BIG_ENDIAN=1 -DPJ_IS_LITTLE_ENDIAN=0" - ac_cflags="$ac_cflags -DPJ_IS_BIG_ENDIAN=1 -DPJ_IS_LITTLE_ENDIAN=0" + CFLAGS="$CFLAGS -DPJ_IS_BIG_ENDIAN=1 -DPJ_IS_LITTLE_ENDIAN=0" + ac_cflags="$ac_cflags -DPJ_IS_BIG_ENDIAN=1 -DPJ_IS_LITTLE_ENDIAN=0" else - CFLAGS="$CFLAGS -DPJ_IS_BIG_ENDIAN=0 -DPJ_IS_LITTLE_ENDIAN=1" - ac_cflags="$ac_cflags -DPJ_IS_BIG_ENDIAN=0 -DPJ_IS_LITTLE_ENDIAN=1" + CFLAGS="$CFLAGS -DPJ_IS_BIG_ENDIAN=0 -DPJ_IS_LITTLE_ENDIAN=1" + ac_cflags="$ac_cflags -DPJ_IS_BIG_ENDIAN=0 -DPJ_IS_LITTLE_ENDIAN=1" fi case $target in *android*) - printf "%s\n" "#define PJ_ANDROID 1" >>confdefs.h + printf "%s\n" "#define PJ_ANDROID 1" >>confdefs.h - ac_target_arch=$TARGET_ABI - ac_std_cpp_lib=$STD_CPP_LIB - ;; + ac_target_arch=$TARGET_ABI + ac_std_cpp_lib=$STD_CPP_LIB + ;; *mingw* | *cygw* | *win32* | *w32* ) - printf "%s\n" "#define PJ_WIN32 1" >>confdefs.h + printf "%s\n" "#define PJ_WIN32 1" >>confdefs.h - printf "%s\n" "#define PJ_WIN32_WINNT 0x0400" >>confdefs.h + printf "%s\n" "#define PJ_WIN32_WINNT 0x0400" >>confdefs.h - printf "%s\n" "#define WIN32_LEAN_AND_MEAN 1" >>confdefs.h + printf "%s\n" "#define WIN32_LEAN_AND_MEAN 1" >>confdefs.h - case $target in - *_64-w64-mingw* ) - printf "%s\n" "#define PJ_WIN64 1" >>confdefs.h + case $target in + *_64-w64-mingw* ) + printf "%s\n" "#define PJ_WIN64 1" >>confdefs.h - ;; - esac - ;; + ;; + esac + ;; *darwin*) - printf "%s\n" "#define PJ_DARWINOS 1" >>confdefs.h + printf "%s\n" "#define PJ_DARWINOS 1" >>confdefs.h - ac_target_arch=$ARCH_VAL - ;; + ac_target_arch=$ARCH_VAL + ;; *linux*) - printf "%s\n" "#define PJ_LINUX 1" >>confdefs.h + printf "%s\n" "#define PJ_LINUX 1" >>confdefs.h - ;; + ;; *bsd*) - printf "%s\n" "#define PJ_BSD 1" >>confdefs.h + printf "%s\n" "#define PJ_BSD 1" >>confdefs.h - ;; + ;; *rtems*) - printf "%s\n" "#define PJ_RTEMS 1" >>confdefs.h + printf "%s\n" "#define PJ_RTEMS 1" >>confdefs.h - ;; + ;; *sunos* | *solaris* ) - printf "%s\n" "#define PJ_SUNOS 1" >>confdefs.h + printf "%s\n" "#define PJ_SUNOS 1" >>confdefs.h - ;; + ;; *) - ;; + ;; esac # Check whether --enable-floating-point was given. if test ${enable_floating_point+y} then : - enableval=$enable_floating_point; if test "$enable_floating_point" = "no"; then - printf "%s\n" "#define PJ_HAS_FLOATING_POINT 0" >>confdefs.h + enableval=$enable_floating_point; + if test "$enable_floating_point" = "no"; then + printf "%s\n" "#define PJ_HAS_FLOATING_POINT 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if floating point is disabled... yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if floating point is disabled... yes" >&5 printf "%s\n" "Checking if floating point is disabled... yes" >&6; } - fi + fi + else $as_nop - printf "%s\n" "#define PJ_HAS_FLOATING_POINT 1" >>confdefs.h + printf "%s\n" "#define PJ_HAS_FLOATING_POINT 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if floating point is disabled... no" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if floating point is disabled... no" >&5 printf "%s\n" "Checking if floating point is disabled... no" >&6; } + fi @@ -5803,18 +5805,18 @@ fi case $target in *mingw* | *cygw* | *win32* | *w32* ) - printf "%s\n" "#define PJ_HAS_ERRNO_H 0" >>confdefs.h + printf "%s\n" "#define PJ_HAS_ERRNO_H 0" >>confdefs.h - ;; + ;; *) - ac_fn_c_check_header_compile "$LINENO" "errno.h" "ac_cv_header_errno_h" "$ac_includes_default" + ac_fn_c_check_header_compile "$LINENO" "errno.h" "ac_cv_header_errno_h" "$ac_includes_default" if test "x$ac_cv_header_errno_h" = xyes then : printf "%s\n" "#define PJ_HAS_ERRNO_H 1" >>confdefs.h fi - ;; + ;; esac ac_fn_c_check_header_compile "$LINENO" "fcntl.h" "ac_cv_header_fcntl_h" "$ac_includes_default" @@ -5866,15 +5868,17 @@ then : fi -ac_fn_c_check_header_compile "$LINENO" "netinet/ip.h" "ac_cv_header_netinet_ip_h" "#if PJ_HAS_SYS_TYPES_H - # include - #endif - #if PJ_HAS_NETINET_IN_SYSTM_H - # include - #endif - #if PJ_HAS_NETINET_IN_H - # include - #endif +ac_fn_c_check_header_compile "$LINENO" "netinet/ip.h" "ac_cv_header_netinet_ip_h" " + #if PJ_HAS_SYS_TYPES_H + # include + #endif + #if PJ_HAS_NETINET_IN_SYSTM_H + # include + #endif + #if PJ_HAS_NETINET_IN_H + # include + #endif + " if test "x$ac_cv_header_netinet_ip_h" = xyes @@ -5883,6 +5887,7 @@ then : fi + ac_fn_c_check_header_compile "$LINENO" "netinet/tcp.h" "ac_cv_header_netinet_tcp_h" "$ac_includes_default" if test "x$ac_cv_header_netinet_tcp_h" = xyes then : @@ -6063,11 +6068,13 @@ then : fi -ac_fn_c_check_header_compile "$LINENO" "mswsock.h" "ac_cv_header_mswsock_h" "#if PJ_HAS_WINSOCK2_H - # include - #elif PJ_HAS_WINSOCK_H - # include - #endif +ac_fn_c_check_header_compile "$LINENO" "mswsock.h" "ac_cv_header_mswsock_h" " + #if PJ_HAS_WINSOCK2_H + # include + #elif PJ_HAS_WINSOCK_H + # include + #endif + " if test "x$ac_cv_header_mswsock_h" = xyes @@ -6076,6 +6083,7 @@ then : fi + ac_fn_c_check_header_compile "$LINENO" "ws2tcpip.h" "ac_cv_header_ws2tcpip_h" "$ac_includes_default" if test "x$ac_cv_header_ws2tcpip_h" = xyes then : @@ -6089,13 +6097,14 @@ then : ac_has_uuid_h=1 fi -ac_fn_c_check_header_compile "$LINENO" "net/if.h" "ac_cv_header_net_if_h" "#if PJ_HAS_SYS_TYPES_H - # include - #endif - #if PJ_HAS_SYS_SOCKET_H +ac_fn_c_check_header_compile "$LINENO" "net/if.h" "ac_cv_header_net_if_h" " + #if PJ_HAS_SYS_TYPES_H + # include + #endif + #if PJ_HAS_SYS_SOCKET_H + # include + #endif - # include - #endif " if test "x$ac_cv_header_net_if_h" = xyes @@ -6106,15 +6115,15 @@ fi case $target in - *android*) - ac_fn_c_check_header_compile "$LINENO" "linux/android_alarm.h" "ac_cv_header_linux_android_alarm_h" "$ac_includes_default" + *android*) + ac_fn_c_check_header_compile "$LINENO" "linux/android_alarm.h" "ac_cv_header_linux_android_alarm_h" "$ac_includes_default" if test "x$ac_cv_header_linux_android_alarm_h" = xyes then : printf "%s\n" "#define PJ_HAS_ANDROID_ALARM_H 1" >>confdefs.h fi - ;; + ;; esac ac_fn_c_check_func "$LINENO" "localtime_r" "ac_cv_func_localtime_r" @@ -6159,9 +6168,11 @@ printf "%s\n" "#define PJ_ATOMIC_VALUE_TYPE long" >>confdefs.h printf %s "checking if inet_aton() is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include - #include - #include + + + #include + #include + #include int main (void) { @@ -6169,16 +6180,20 @@ inet_aton(0, 0); ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - printf "%s\n" "#define PJ_SOCK_HAS_INET_ATON 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "#define PJ_SOCK_HAS_INET_ATON 1" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6186,9 +6201,11 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext printf %s "checking if inet_pton() is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include - #include - #include + + + #include + #include + #include int main (void) { @@ -6196,16 +6213,20 @@ inet_pton(0, 0, 0); ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - printf "%s\n" "#define PJ_SOCK_HAS_INET_PTON 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "#define PJ_SOCK_HAS_INET_PTON 1" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6213,9 +6234,11 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext printf %s "checking if inet_ntop() is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include - #include - #include + + + #include + #include + #include int main (void) { @@ -6223,16 +6246,20 @@ inet_ntop(0, 0, 0, 0); ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - printf "%s\n" "#define PJ_SOCK_HAS_INET_NTOP 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "#define PJ_SOCK_HAS_INET_NTOP 1" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6240,9 +6267,11 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext printf %s "checking if getaddrinfo() is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include - #include - #include + + + #include + #include + #include int main (void) { @@ -6250,16 +6279,20 @@ getaddrinfo(0, 0, 0, 0); ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - printf "%s\n" "#define PJ_SOCK_HAS_GETADDRINFO 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "#define PJ_SOCK_HAS_GETADDRINFO 1" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6267,8 +6300,10 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext printf %s "checking if socketpair() is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include - #include + + + #include + #include int main (void) { @@ -6276,16 +6311,20 @@ socketpair(0, 0, 0, 0); ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - printf "%s\n" "#define PJ_SOCK_HAS_SOCKETPAIR 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "#define PJ_SOCK_HAS_SOCKETPAIR 1" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6293,10 +6332,12 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext printf %s "checking if sockaddr_in has sin_len member... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include - #include - #include - #include + + + #include + #include + #include + #include int main (void) { @@ -6304,16 +6345,20 @@ struct sockaddr_in a; a.sin_len=0; ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - printf "%s\n" "#define PJ_SOCKADDR_HAS_LEN 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "#define PJ_SOCKADDR_HAS_LEN 1" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6321,8 +6366,10 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext printf %s "checking if socklen_t is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include - #include + + + #include + #include int main (void) { @@ -6330,16 +6377,20 @@ socklen_t xxx = 0; ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - printf "%s\n" "#define PJ_HAS_SOCKLEN_T 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "#define PJ_HAS_SOCKLEN_T 1" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6347,8 +6398,10 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext printf %s "checking if IPV6_V6ONLY is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include - #include + + + #include + #include int main (void) { @@ -6356,16 +6409,20 @@ int opt = IPV6_V6ONLY; ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - printf "%s\n" "#define PJ_SOCK_HAS_IPV6_V6ONLY 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "#define PJ_SOCK_HAS_IPV6_V6ONLY 1" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6373,18 +6430,20 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext printf %s "checking if SO_ERROR is available... " >&6; } case $target in *mingw* | *cygw* | *win32* | *w32* ) - printf "%s\n" "#define PJ_HAS_SO_ERROR 1" >>confdefs.h + printf "%s\n" "#define PJ_HAS_SO_ERROR 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - ;; + ;; *) - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include - #include - #include - #include + + + #include + #include + #include + #include int main (void) { @@ -6392,19 +6451,23 @@ int i=SO_ERROR; ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - printf "%s\n" "#define PJ_HAS_SO_ERROR 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "#define PJ_HAS_SO_ERROR 1" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - ;; + ;; esac @@ -6412,7 +6475,8 @@ esac printf %s "checking if pthread_rwlock_t is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #include int main (void) { @@ -6420,20 +6484,26 @@ pthread_rwlock_t *x; ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - printf "%s\n" "#define PJ_EMULATE_RWMUTEX 0" >>confdefs.h - ac_rwmutex="yes" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "#define PJ_EMULATE_RWMUTEX 0" >>confdefs.h + + ac_rwmutex="yes" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } + else $as_nop - printf "%s\n" "#define PJ_EMULATE_RWMUTEX 1" >>confdefs.h - ac_rwmutex="no" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 + printf "%s\n" "#define PJ_EMULATE_RWMUTEX 1" >>confdefs.h + + ac_rwmutex="no" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6442,8 +6512,10 @@ if test "$ac_rwmutex" = "no"; then printf %s "checking if pthread_rwlock_t is available with _POSIX_READER_WRITER_LOCKS... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#define _POSIX_READER_WRITER_LOCKS - #include + + + #define _POSIX_READER_WRITER_LOCKS + #include int main (void) { @@ -6451,19 +6523,25 @@ pthread_rwlock_t *x; ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - printf "%s\n" "#define PJ_EMULATE_RWMUTEX 0" >>confdefs.h - CFLAGS="$CFLAGS -D_POSIX_THREADS -D_POSIX_READER_WRITER_LOCKS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "#define PJ_EMULATE_RWMUTEX 0" >>confdefs.h + + CFLAGS="$CFLAGS -D_POSIX_THREADS -D_POSIX_READER_WRITER_LOCKS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } + else $as_nop - printf "%s\n" "#define PJ_EMULATE_RWMUTEX 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 + printf "%s\n" "#define PJ_EMULATE_RWMUTEX 1" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi @@ -6472,7 +6550,8 @@ fi printf %s "checking if pthread_mutexattr_settype() is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #include int main (void) { @@ -6480,16 +6559,20 @@ pthread_mutexattr_settype(0,PTHREAD_MUTEX_FAST_NP); ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - printf "%s\n" "#define PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "#define PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE 1" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6497,25 +6580,29 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext printf %s "checking if pthread_mutexattr_t has recursive member... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #include int main (void) { -pthread_mutexattr_t attr; - attr.recursive=1; +pthread_mutexattr_t attr; attr.recursive=1; ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - printf "%s\n" "#define PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "#define PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE 1" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6528,65 +6615,69 @@ ac_os_objs=ioqueue_select.o ac_linux_poll=select case $target in - *darwin* | *bsd*) - # Check whether --enable-kqueue was given. + *darwin* | *bsd*) + # Check whether --enable-kqueue was given. if test ${enable_kqueue+y} then : enableval=$enable_kqueue; - if test "$enable_kqueue" = "yes"; then - ac_os_objs=ioqueue_kqueue.o - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: kqueue()" >&5 + if test "$enable_kqueue" = "yes"; then + ac_os_objs=ioqueue_kqueue.o + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: kqueue()" >&5 printf "%s\n" "kqueue()" >&6; } - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: select()" >&5 + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: select()" >&5 printf "%s\n" "select()" >&6; } - fi + fi else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: select()" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: select()" >&5 printf "%s\n" "select()" >&6; } + fi - ;; - *) - # Check whether --enable-epoll was given. + ;; + *) + # Check whether --enable-epoll was given. if test ${enable_epoll+y} then : enableval=$enable_epoll; - if test "$enable_epoll" = "yes"; then - ac_os_objs=ioqueue_epoll.o - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: /dev/epoll" >&5 + if test "$enable_epoll" = "yes"; then + ac_os_objs=ioqueue_epoll.o + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: /dev/epoll" >&5 printf "%s\n" "/dev/epoll" >&6; } - printf "%s\n" "#define PJ_HAS_LINUX_EPOLL 1" >>confdefs.h + printf "%s\n" "#define PJ_HAS_LINUX_EPOLL 1" >>confdefs.h - ac_linux_poll=epoll - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: select()" >&5 + ac_linux_poll=epoll + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: select()" >&5 printf "%s\n" "select()" >&6; } - fi + fi else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: select()" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: select()" >&5 printf "%s\n" "select()" >&6; } + fi - ;; + ;; esac # Check whether --enable-shared was given. if test ${enable_shared+y} then : - enableval=$enable_shared; if test "$enable_shared" = "yes"; then - ac_shared_libraries=1 - CFLAGS="$CFLAGS -fPIC" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Building shared libraries... yes" >&5 + enableval=$enable_shared; + if test "$enable_shared" = "yes"; then + ac_shared_libraries=1 + CFLAGS="$CFLAGS -fPIC" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Building shared libraries... yes" >&5 printf "%s\n" "Building shared libraries... yes" >&6; } - fi + fi + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Building shared libraries... no" >&5 printf "%s\n" "Building shared libraries... no" >&6; } @@ -6598,11 +6689,13 @@ fi # Check whether --enable-pjsua2 was given. if test ${enable_pjsua2+y} then : - enableval=$enable_pjsua2; if test "$enable_pjsua2" = "no"; then - ac_no_pjsua2=1 - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Building pjsua2 library and application... no" >&5 + enableval=$enable_pjsua2; + if test "$enable_pjsua2" = "no"; then + ac_no_pjsua2=1 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Building pjsua2 library and application... no" >&5 printf "%s\n" "Building pjsua2 library and application... no" >&6; } - fi + fi + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Building pjsua2 library and application... yes" >&5 printf "%s\n" "Building pjsua2 library and application... yes" >&6; } @@ -6612,59 +6705,61 @@ fi case $target in - *mingw* | *cygw* | *win32* | *w32* ) - ac_os_objs="$ac_os_objs file_access_win32.o file_io_win32.o os_core_win32.o os_error_win32.o os_time_win32.o os_timestamp_win32.o guid_win32.o sock_qos_bsd.o unicode_win32.o" - ;; - *) - ac_os_objs="$ac_os_objs file_access_unistd.o file_io_ansi.o os_core_unix.o os_error_unix.o os_time_unix.o os_timestamp_posix.o" - case $target in - *-apple-darwin_ios*) - ac_os_objs="$ac_os_objs os_info_iphone.o os_core_darwin.o" - ;; - *darwin*) - ac_os_objs="$ac_os_objs os_core_darwin.o" - ;; - esac - # QoS - case $target in - *darwin*) - ac_os_objs="$ac_os_objs sock_qos_darwin.o sock_qos_bsd.o" - ;; - *) - ac_os_objs="$ac_os_objs sock_qos_bsd.o" - ;; - esac - # SSL - case $target in - *darwin*) - ac_os_objs="$ac_os_objs ssl_sock_apple.o" - ;; - esac - # UUID - case $target in - *android*) - ac_os_objs="$ac_os_objs guid_android.o" - ;; - *darwin*) - ac_os_objs="$ac_os_objs guid_darwin.o" - ;; - *bsd*) - ac_os_objs="$ac_os_objs guid_bsd.o" - ;; - *) - if test "$ac_has_uuid_lib" = "1" -a "$ac_has_uuid_h" = "1"; then - ac_os_objs="$ac_os_objs guid_uuid.o" - else - ac_os_objs="$ac_os_objs guid_simple.o" - fi - ;; - esac - ;; + *mingw* | *cygw* | *win32* | *w32* ) + ac_os_objs="$ac_os_objs file_access_win32.o file_io_win32.o os_core_win32.o os_error_win32.o os_time_win32.o os_timestamp_win32.o guid_win32.o sock_qos_bsd.o unicode_win32.o" + ;; + *) + ac_os_objs="$ac_os_objs file_access_unistd.o file_io_ansi.o os_core_unix.o os_error_unix.o os_time_unix.o os_timestamp_posix.o" + case $target in + *-apple-darwin_ios*) + ac_os_objs="$ac_os_objs os_info_iphone.o os_core_darwin.o" + ;; + *darwin*) + ac_os_objs="$ac_os_objs os_core_darwin.o" + ;; + esac + + # QoS + case $target in + *darwin*) + ac_os_objs="$ac_os_objs sock_qos_darwin.o sock_qos_bsd.o" + ;; + *) + ac_os_objs="$ac_os_objs sock_qos_bsd.o" + ;; + esac + + # SSL + case $target in + *darwin*) + ac_os_objs="$ac_os_objs ssl_sock_apple.o" + ;; + esac + + # UUID + case $target in + *android*) + ac_os_objs="$ac_os_objs guid_android.o" + ;; + *darwin*) + ac_os_objs="$ac_os_objs guid_darwin.o" + ;; + *bsd*) + ac_os_objs="$ac_os_objs guid_bsd.o" + ;; + *) + if test "$ac_has_uuid_lib" = "1" -a "$ac_has_uuid_h" = "1"; then + ac_os_objs="$ac_os_objs guid_uuid.o" + else + ac_os_objs="$ac_os_objs guid_simple.o" + fi + ;; + esac + ;; esac - # Check whether --with-upnp was given. if test ${with_upnp+y} then : @@ -6683,64 +6778,68 @@ fi if test ${enable_upnp+y} then : enableval=$enable_upnp; - if test "$enable_upnp" = "no"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if UPnP is disabled... yes" >&5 + if test "$enable_upnp" = "no"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if UPnP is disabled... yes" >&5 printf "%s\n" "Checking if UPnP is disabled... yes" >&6; } - fi + fi else $as_nop - if test "x$with_upnp" != "xno" -a "x$with_upnp" != "x"; then - UPNP_PREFIX=$with_upnp - UPNP_CFLAGS="-I$UPNP_PREFIX/upnp/inc -I$UPNP_PREFIX/ixml/inc" - UPNP_LDFLAGS="-L$UPNP_PREFIX/upnp/.libs -L$UPNP_PREFIX/imxl/.libs" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using UPnP prefix... $with_upnp" >&5 + if test "x$with_upnp" != "xno" -a "x$with_upnp" != "x"; then + UPNP_PREFIX=$with_upnp + UPNP_CFLAGS="-I$UPNP_PREFIX/upnp/inc -I$UPNP_PREFIX/ixml/inc" + UPNP_LDFLAGS="-L$UPNP_PREFIX/upnp/.libs -L$UPNP_PREFIX/imxl/.libs" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using UPnP prefix... $with_upnp" >&5 printf "%s\n" "Using UPnP prefix... $with_upnp" >&6; } - else - UPNP_CFLAGS="" - UPNP_LDFLAGS="" - fi + else + UPNP_CFLAGS="" + UPNP_LDFLAGS="" + fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking UPnP usability" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking UPnP usability" >&5 printf %s "checking UPnP usability... " >&6; } - UPNP_LIBS="-lupnp -lixml" + UPNP_LIBS="-lupnp -lixml" - SAVED_LIBS="$LIBS" - SAVED_LDFLAGS="$LDFLAGS" - SAVED_CFLAGS="$CFLAGS" + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CFLAGS="$CFLAGS" - LIBS="$UPNP_LIBS $LIBS" - LDFLAGS="$UPNP_LDFLAGS $LDFLAGS" - CFLAGS="$UPNP_CFLAGS $CFLAGS" + LIBS="$UPNP_LIBS $LIBS" + LDFLAGS="$UPNP_LDFLAGS $LDFLAGS" + CFLAGS="$UPNP_CFLAGS $CFLAGS" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + + #include int main (void) { UpnpInit2(NULL, 0); - ; return 0; } + _ACEOF if ac_fn_c_try_link "$LINENO" then : - CFLAGS="$CFLAGS -DPJNATH_HAS_UPNP=1 $UPNP_CFLAGS" - LDFLAGS="$LDFLAGS $UPNP_LDFLAGS $UPNP_LIBS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + + CFLAGS="$CFLAGS -DPJNATH_HAS_UPNP=1 $UPNP_CFLAGS" + LDFLAGS="$LDFLAGS $UPNP_LDFLAGS $UPNP_LIBS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop - LIBS="$SAVED_LIBS" - LDFLAGS="$SAVED_LDFLAGS" - CFLAGS="$SAVED_CFLAGS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CFLAGS="$SAVED_CFLAGS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -6757,15 +6856,16 @@ ac_external_speex=0 if test ${with_external_speex+y} then : withval=$with_external_speex; - if test "x$with_external_speex" != "xno"; then - # Test Speex installation - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external Speex devkit is installed" >&5 + if test "x$with_external_speex" != "xno"; then + # Test Speex installation + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external Speex devkit is installed" >&5 printf %s "checking if external Speex devkit is installed... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include - #include + + #include + #include int main (void) { @@ -6773,20 +6873,25 @@ speex_echo_state_init(0, 0); speex_encoder_init(0); ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes!!" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes!!" >&5 printf "%s\n" "yes!!" >&6; } - printf "%s\n" "#define PJMEDIA_EXTERNAL_SPEEX_CODEC 1" >>confdefs.h + printf "%s\n" "#define PJMEDIA_EXTERNAL_SPEEX_CODEC 1" >>confdefs.h - ac_external_speex="1" + ac_external_speex="1" else $as_nop - as_fn_error $? "Unable to use external Speex library. If Speex development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 + + as_fn_error $? "Unable to use external Speex library. If Speex development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 + + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - fi + fi fi @@ -6799,13 +6904,14 @@ ac_external_gsm=0 if test ${with_external_gsm+y} then : withval=$with_external_gsm; - if test "x$with_external_gsm" != "xno"; then - # Test GSM library installation - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external GSM devkit is installed as gsm/gsm.h" >&5 + if test "x$with_external_gsm" != "xno"; then + # Test GSM library installation + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external GSM devkit is installed as gsm/gsm.h" >&5 printf %s "checking if external GSM devkit is installed as gsm/gsm.h... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #include int main (void) { @@ -6813,26 +6919,30 @@ gsm_create(); ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes!!" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes!!" >&5 printf "%s\n" "yes!!" >&6; } - printf "%s\n" "#define PJMEDIA_EXTERNAL_GSM_CODEC 1" >>confdefs.h + printf "%s\n" "#define PJMEDIA_EXTERNAL_GSM_CODEC 1" >>confdefs.h - printf "%s\n" "#define PJMEDIA_EXTERNAL_GSM_GSM_H 1" >>confdefs.h + printf "%s\n" "#define PJMEDIA_EXTERNAL_GSM_GSM_H 1" >>confdefs.h - ac_external_gsm="1" + ac_external_gsm="1" else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external GSM devkit is installed as gsm.h" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external GSM devkit is installed as gsm.h" >&5 printf %s "checking if external GSM devkit is installed as gsm.h... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + + #include int main (void) { @@ -6840,19 +6950,22 @@ gsm_create(); ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes!!" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes!!" >&5 printf "%s\n" "yes!!" >&6; } - printf "%s\n" "#define PJMEDIA_EXTERNAL_GSM_CODEC 1" >>confdefs.h + printf "%s\n" "#define PJMEDIA_EXTERNAL_GSM_CODEC 1" >>confdefs.h - printf "%s\n" "#define PJMEDIA_EXTERNAL_GSM_H 1" >>confdefs.h + printf "%s\n" "#define PJMEDIA_EXTERNAL_GSM_H 1" >>confdefs.h - ac_external_gsm="1" + ac_external_gsm="1" else $as_nop - as_fn_error $? "Unable to use external GSM library. If GSM development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 + + as_fn_error $? "Unable to use external GSM library. If GSM development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 fi @@ -6861,7 +6974,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - fi + fi fi @@ -6876,14 +6989,15 @@ ac_external_srtp=0 if test ${with_external_srtp+y} then : withval=$with_external_srtp; - if test "x$with_external_srtp" != "xno"; then - # Test SRTP installation - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external SRTP devkit is installed" >&5 + if test "x$with_external_srtp" != "xno"; then + # Test SRTP installation + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external SRTP devkit is installed" >&5 printf %s "checking if external SRTP devkit is installed... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #include int main (void) { @@ -6891,19 +7005,23 @@ srtp_init(); ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes: version 2.x" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes: version 2.x" >&5 printf "%s\n" "yes: version 2.x" >&6; } - ac_external_srtp="2" - ac_external_srtp_lib="srtp2" + ac_external_srtp="2" + ac_external_srtp_lib="srtp2" else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #include int main (void) { @@ -6911,32 +7029,39 @@ srtp_init(); ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes: version 1.x" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes: version 1.x" >&5 printf "%s\n" "yes: version 1.x" >&6; } - ac_external_srtp="1" - ac_external_srtp_lib="srtp" + ac_external_srtp="1" + ac_external_srtp_lib="srtp" else $as_nop - as_fn_error $? "Unable to use SRTP. If SRTP development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 + + as_fn_error $? "Unable to use SRTP. If SRTP development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 + + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - fi + fi fi if test "x$ac_external_srtp" != "x0"; then - ac_srtp_deinit_present=0 + ac_srtp_deinit_present=0 - ac_srtp_shutdown_present=0 + ac_srtp_shutdown_present=0 - as_ac_Lib=`printf "%s\n" "ac_cv_lib_$ac_external_srtp_lib""_srtp_deinit" | $as_tr_sh` + as_ac_Lib=`printf "%s\n" "ac_cv_lib_$ac_external_srtp_lib""_srtp_deinit" | $as_tr_sh` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for srtp_deinit in -l$ac_external_srtp_lib" >&5 printf %s "checking for srtp_deinit in -l$ac_external_srtp_lib... " >&6; } if eval test \${$as_ac_Lib+y} @@ -6978,8 +7103,8 @@ then : ac_srtp_deinit_present=1 fi - if test "x$ac_srtp_deinit_present" != "x1"; then - as_ac_Lib=`printf "%s\n" "ac_cv_lib_$ac_external_srtp_lib""_srtp_shutdown" | $as_tr_sh` + if test "x$ac_srtp_deinit_present" != "x1"; then + as_ac_Lib=`printf "%s\n" "ac_cv_lib_$ac_external_srtp_lib""_srtp_shutdown" | $as_tr_sh` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for srtp_shutdown in -l$ac_external_srtp_lib" >&5 printf %s "checking for srtp_shutdown in -l$ac_external_srtp_lib... " >&6; } if eval test \${$as_ac_Lib+y} @@ -7021,10 +7146,9 @@ then : ac_srtp_shutdown_present=1 fi - fi + fi fi - ac_external_yuv=0 @@ -7032,13 +7156,15 @@ ac_external_yuv=0 if test ${with_external_yuv+y} then : withval=$with_external_yuv; - if test "x$with_external_yuv" != "xno"; then - # Test libyuv installation - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external libyuv devkit is installed" >&5 + if test "x$with_external_yuv" != "xno"; then + # Test libyuv installation + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external libyuv devkit is installed" >&5 printf %s "checking if external libyuv devkit is installed... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + + #include int main (void) { @@ -7046,18 +7172,23 @@ RGB24ToI420(0,0,0,0,0,0,0,0,0,0); ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes!!" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes!!" >&5 printf "%s\n" "yes!!" >&6; } - ac_external_yuv="1" + ac_external_yuv="1" else $as_nop - as_fn_error $? "Unable to use external libyuv. If libyuv development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 + + as_fn_error $? "Unable to use external libyuv. If libyuv development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 + + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - fi + fi fi @@ -7071,15 +7202,16 @@ ac_external_webrtc=0 if test ${with_external_webrtc+y} then : withval=$with_external_webrtc; - if test "x$with_external_webrtc" != "xno"; then - # Test webrtc installation - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external webrtc devkit is installed" >&5 + if test "x$with_external_webrtc" != "xno"; then + # Test webrtc installation + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external webrtc devkit is installed" >&5 printf %s "checking if external webrtc devkit is installed... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include - #include + + #include + #include int main (void) { @@ -7087,18 +7219,23 @@ WebRtcAec_Create(); ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes!!" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes!!" >&5 printf "%s\n" "yes!!" >&6; } - ac_external_webrtc="1" + ac_external_webrtc="1" else $as_nop - as_fn_error $? "Unable to use external webrtc. If webrtc development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 + + as_fn_error $? "Unable to use external webrtc. If webrtc development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 + + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - fi + fi fi @@ -7112,14 +7249,15 @@ ac_external_webrtc_aec3=0 if test ${with_external_webrtc_aec3+y} then : withval=$with_external_webrtc_aec3; - if test "x$with_external_webrtc_aec3" != "xno"; then - # Test webrtc AEC3 installation - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external webrtc AEC3 is installed" >&5 + if test "x$with_external_webrtc_aec3" != "xno"; then + # Test webrtc AEC3 installation + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external webrtc AEC3 is installed" >&5 printf %s "checking if external webrtc AEC3 is installed... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include "modules/audio_processing/aec3/echo_canceller3.h" + + #include "modules/audio_processing/aec3/echo_canceller3.h" int main (void) { @@ -7127,18 +7265,23 @@ EchoCanceller3 ec(EchoCanceller3Config(), 16000, 1, 1); ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes!!" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes!!" >&5 printf "%s\n" "yes!!" >&6; } - ac_external_webrtc_aec3="1" + ac_external_webrtc_aec3="1" else $as_nop - as_fn_error $? "Unable to use external webrtc AEC3. If webrtc development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 + + as_fn_error $? "Unable to use external webrtc AEC3. If webrtc development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 + + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - fi + fi fi @@ -7150,11 +7293,13 @@ ac_pjmedia_resample=libresample # Check whether --enable-resample was given. if test ${enable_resample+y} then : - enableval=$enable_resample; if test "$enable_resample" = "no"; then - ac_pjmedia_resample=none - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if resampling is disabled...yes" >&5 + enableval=$enable_resample; + if test "$enable_resample" = "no"; then + ac_pjmedia_resample=none + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if resampling is disabled...yes" >&5 printf "%s\n" "Checking if resampling is disabled...yes" >&6; } - fi + fi + fi @@ -7163,11 +7308,13 @@ fi # Check whether --enable-sound was given. if test ${enable_sound+y} then : - enableval=$enable_sound; if test "$enable_sound" = "no"; then - ac_pjmedia_snd=null - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if sound is disabled... yes" >&5 + enableval=$enable_sound; + if test "$enable_sound" = "no"; then + ac_pjmedia_snd=null + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if sound is disabled... yes" >&5 printf "%s\n" "Checking if sound is disabled... yes" >&6; } - fi + fi + fi @@ -7179,14 +7326,15 @@ ac_external_pa=0 if test ${with_external_pa+y} then : withval=$with_external_pa; - if test "x$with_external_pa" != "xno"; then - # Test PortAudio installation - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external PortAudio devkit is installed" >&5 + if test "x$with_external_pa" != "xno"; then + # Test PortAudio installation + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external PortAudio devkit is installed" >&5 printf %s "checking if external PortAudio devkit is installed... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #include int main (void) { @@ -7194,18 +7342,23 @@ Pa_Initialize(); ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes!!" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes!!" >&5 printf "%s\n" "yes!!" >&6; } - ac_external_pa="1" + ac_external_pa="1" else $as_nop - as_fn_error $? "Unable to use PortAudio. If PortAudio development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 + + as_fn_error $? "Unable to use PortAudio. If PortAudio development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 + + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - fi + fi fi @@ -7232,87 +7385,90 @@ fi if test "x$ac_cv_c_bigendian" = "xyes"; then - ac_pa_cflags="$ac_pa_cflags -DPA_BIG_ENDIAN" + ac_pa_cflags="$ac_pa_cflags -DPA_BIG_ENDIAN" else - ac_pa_cflags="$ac_pa_cflags -DPA_LITTLE_ENDIAN" + ac_pa_cflags="$ac_pa_cflags -DPA_LITTLE_ENDIAN" fi - # Check whether --with-oboe was given. if test ${with_oboe+y} then : withval=$with_oboe; - if test "x$with_oboe" != "xno" -a "x$with_oboe" != "x"; then - OBOE_PREFIX=$with_oboe - OBOE_CFLAGS="-I$OBOE_PREFIX/prefab/modules/oboe/include" - OBOE_LDFLAGS="-L$OBOE_PREFIX/prefab/modules/oboe/libs/android.$TARGET_ABI" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using Oboe prefix... $with_oboe" >&5 + if test "x$with_oboe" != "xno" -a "x$with_oboe" != "x"; then + OBOE_PREFIX=$with_oboe + OBOE_CFLAGS="-I$OBOE_PREFIX/prefab/modules/oboe/include" + OBOE_LDFLAGS="-L$OBOE_PREFIX/prefab/modules/oboe/libs/android.$TARGET_ABI" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using Oboe prefix... $with_oboe" >&5 printf "%s\n" "Using Oboe prefix... $with_oboe" >&6; } - else - OBOE_CFLAGS="" - OBOE_LDFLAGS="" - fi + else + OBOE_CFLAGS="" + OBOE_LDFLAGS="" + fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking Oboe usability" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking Oboe usability" >&5 printf %s "checking Oboe usability... " >&6; } - OBOE_LIBS="-loboe -lOpenSLES -llog" + OBOE_LIBS="-loboe -lOpenSLES -llog" - SAVED_LIBS="$LIBS" - SAVED_LDFLAGS="$LDFLAGS" - SAVED_CXXFLAGS="$CXXFLAGS" + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CXXFLAGS="$CXXFLAGS" - LIBS="$OBOE_LIBS $LIBS" - LDFLAGS="$OBOE_LDFLAGS $LDFLAGS" - CXXFLAGS="$OBOE_CFLAGS $CXXFLAGS" + LIBS="$OBOE_LIBS $LIBS" + LDFLAGS="$OBOE_LDFLAGS $LDFLAGS" + CXXFLAGS="$OBOE_CFLAGS $CXXFLAGS" - ac_ext=cpp + ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + + #include int main (void) { oboe::AudioStreamBuilder sb; sb.setDeviceId(0); - ; return 0; } + _ACEOF if ac_fn_cxx_try_link "$LINENO" then : - ac_oboe_cflags="-DPJMEDIA_AUDIO_DEV_HAS_OBOE=1 $OBOE_CFLAGS" - ac_oboe_ldflags="$OBOE_LDFLAGS $OBOE_LIBS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + + ac_oboe_cflags="-DPJMEDIA_AUDIO_DEV_HAS_OBOE=1 $OBOE_CFLAGS" + ac_oboe_ldflags="$OBOE_LDFLAGS $OBOE_LIBS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop - LIBS="$SAVED_LIBS" - LDFLAGS="$SAVED_LDFLAGS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - ac_ext=c + ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu - # Put back original CXXFLAGS as app won't needed it. - CXXFLAGS="$SAVED_CFLAGS" + # Put back original CXXFLAGS as app won't needed it. + CXXFLAGS="$SAVED_CFLAGS" fi @@ -7320,126 +7476,129 @@ fi if test "$enable_sound" = "no"; then - true; + true; else - case $target in - *android*) - if test "x$ac_oboe_cflags" != "x"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... Oboe" >&5 + case $target in + *android*) + if test "x$ac_oboe_cflags" != "x"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... Oboe" >&5 printf "%s\n" "Checking sound device backend... Oboe" >&6; } - else - LIBS="$LIBS -lOpenSLES" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... Android" >&5 + else + LIBS="$LIBS -lOpenSLES" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... Android" >&5 printf "%s\n" "Checking sound device backend... Android" >&6; } - fi - ;; - *-apple-darwin_ios*) - LIBS="$LIBS -framework CoreAudio -framework CoreFoundation -framework AudioToolbox -framework CFNetwork -framework UIKit -framework AVFoundation" - ac_pjmedia_audiodev_objs="coreaudio_dev.o" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... AudioUnit" >&5 + fi + ;; + *-apple-darwin_ios*) + LIBS="$LIBS -framework CoreAudio -framework CoreFoundation -framework AudioToolbox -framework CFNetwork -framework UIKit -framework AVFoundation" + ac_pjmedia_audiodev_objs="coreaudio_dev.o" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... AudioUnit" >&5 printf "%s\n" "Checking sound device backend... AudioUnit" >&6; } - ;; - *darwin*) - LIBS="$LIBS -framework CoreAudio -framework CoreServices -framework AudioUnit -framework AudioToolbox" - ac_pjmedia_audiodev_objs="coreaudio_dev.o" - if test "`uname -r`" = "6.8"; then - #ac_pa_cflags="$ac_pa_cflags -DPA_OLD_CORE_AUDIO -DMOSX_USE_NON_ATOMIC_FLAG_BITS" - #AC_MSG_RESULT([Setting additional PortAudio CFLAGS.. -DPA_OLD_CORE_AUDIO -DMOSX_USE_NON_ATOMIC_FLAG_BITS]) - #ac_pjmedia_snd=pa_old_darwinos - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... old coreaudio" >&5 + ;; + *darwin*) + LIBS="$LIBS -framework CoreAudio -framework CoreServices -framework AudioUnit -framework AudioToolbox" + ac_pjmedia_audiodev_objs="coreaudio_dev.o" + if test "`uname -r`" = "6.8"; then + #ac_pa_cflags="$ac_pa_cflags -DPA_OLD_CORE_AUDIO -DMOSX_USE_NON_ATOMIC_FLAG_BITS" + #AC_MSG_RESULT([Setting additional PortAudio CFLAGS.. -DPA_OLD_CORE_AUDIO -DMOSX_USE_NON_ATOMIC_FLAG_BITS]) + #ac_pjmedia_snd=pa_old_darwinos + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... old coreaudio" >&5 printf "%s\n" "Checking sound device backend... old coreaudio" >&6; } - else - ac_pjmedia_snd=coreaudio - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... coreaudio" >&5 + else + ac_pjmedia_snd=coreaudio + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... coreaudio" >&5 printf "%s\n" "Checking sound device backend... coreaudio" >&6; } - fi - ;; - *cygwin* | *mingw*) - ac_pjmedia_snd=win32 - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... win32 sound" >&5 + fi + ;; + *cygwin* | *mingw*) + ac_pjmedia_snd=win32 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... win32 sound" >&5 printf "%s\n" "Checking sound device backend... win32 sound" >&6; } - ;; - *rtems*) - ac_pjmedia_snd=null - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... null sound" >&5 + ;; + *rtems*) + ac_pjmedia_snd=null + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... null sound" >&5 printf "%s\n" "Checking sound device backend... null sound" >&6; } - ;; - *) - if test "x$ac_external_pa" != "x1"; then - ac_fn_c_check_header_compile "$LINENO" "alsa/version.h" "ac_cv_header_alsa_version_h" "$ac_includes_default" + ;; + *) + if test "x$ac_external_pa" != "x1"; then + ac_fn_c_check_header_compile "$LINENO" "alsa/version.h" "ac_cv_header_alsa_version_h" "$ac_includes_default" if test "x$ac_cv_header_alsa_version_h" = xyes then : LIBS="$LIBS -lasound" - ac_pjmedia_snd=alsa + ac_pjmedia_snd=alsa fi - if test "x$ac_pjmedia_snd" = "xalsa"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... alsa" >&5 + if test "x$ac_pjmedia_snd" = "xalsa"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... alsa" >&5 printf "%s\n" "Checking sound device backend... alsa" >&6; } - else - ac_pjmedia_snd=null - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... null sound" >&5 + else + ac_pjmedia_snd=null + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... null sound" >&5 printf "%s\n" "Checking sound device backend... null sound" >&6; } - fi - fi - ;; - esac + fi + fi + ;; + esac fi # Disable video on mingw by default (but respect --enable-video=yes) case $target in - *mingw*) - if test ! "$enable_video" = "yes"; then - enable_video="no" - fi - ;; + *mingw*) + if test ! "$enable_video" = "yes"; then + enable_video="no" + fi + ;; esac # Check whether --enable-video was given. if test ${enable_video+y} then : - enableval=$enable_video; if test "$enable_video" = "no"; then - #AC_DEFINE(PJMEDIA_HAS_VIDEO,0) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Video is disabled" >&5 + enableval=$enable_video; + if test "$enable_video" = "no"; then + #AC_DEFINE(PJMEDIA_HAS_VIDEO,0) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Video is disabled" >&5 printf "%s\n" "Video is disabled" >&6; } - enable_sdl="no" - enable_ffmpeg="no" - enable_v4l2="no" - enable_openh264="no" - enable_libyuv="no" - enable_vpx="no" - fi + enable_sdl="no" + enable_ffmpeg="no" + enable_v4l2="no" + enable_openh264="no" + enable_libyuv="no" + enable_vpx="no" + fi + fi case $target in - *android*) - LIBS="$LIBS -llog" - ;; - *-apple-darwin_ios*) - LIBS="$LIBS -framework UIKit" - ;; - *darwin*) - LIBS="$LIBS -framework Foundation -framework AppKit" - ;; + *android*) + LIBS="$LIBS -llog" + ;; + *-apple-darwin_ios*) + LIBS="$LIBS -framework UIKit" + ;; + *darwin*) + LIBS="$LIBS -framework Foundation -framework AppKit" + ;; esac if test "$enable_video" = "no"; then - true; + true; else - case $target in - *android*) - ac_pjmedia_video=android_os + case $target in + *android*) + ac_pjmedia_video=android_os - SAVED_LIBS="$LIBS" - LIBS="-lGLESv2 -lEGL -landroid -lc" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + SAVED_LIBS="$LIBS" + LIBS="-lGLESv2 -lEGL -landroid -lc" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ + int main (void) { @@ -7447,49 +7606,52 @@ main (void) ; return 0; } + _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_pjmedia_video_has_android=yes else $as_nop ac_pjmedia_video_has_android=no + fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - LIBS="$SAVED_LIBS" - if test "$ac_pjmedia_video_has_android" = "yes"; then - ac_android_cflags="-DPJMEDIA_VIDEO_DEV_HAS_ANDROID_OPENGL=1" - LIBS="$LIBS -lGLESv2 -lEGL -landroid" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if OpenGL ES 2 is available... yes" >&5 + LIBS="$SAVED_LIBS" + if test "$ac_pjmedia_video_has_android" = "yes"; then + ac_android_cflags="-DPJMEDIA_VIDEO_DEV_HAS_ANDROID_OPENGL=1" + LIBS="$LIBS -lGLESv2 -lEGL -landroid" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if OpenGL ES 2 is available... yes" >&5 printf "%s\n" "Checking if OpenGL ES 2 is available... yes" >&6; } - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if OpenGL ES 2 is available... no" >&5 + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if OpenGL ES 2 is available... no" >&5 printf "%s\n" "Checking if OpenGL ES 2 is available... no" >&6; } - fi - ac_android_cflags="$ac_android_cflags -DPJMEDIA_VIDEO_DEV_HAS_ANDROID=1" - ;; - *mingw*) - if test "$enable_video" = "yes"; then - ac_pjmedia_video=windows_os + fi + ac_android_cflags="$ac_android_cflags -DPJMEDIA_VIDEO_DEV_HAS_ANDROID=1" + ;; + *mingw*) + if test "$enable_video" = "yes"; then + ac_pjmedia_video=windows_os - ac_pjmedia_video_dev_has_dshow=yes + ac_pjmedia_video_dev_has_dshow=yes - ac_dshow_cflags="-DPJMEDIA_HAS_VIDEO=1 -DPJMEDIA_VIDEO_DEV_HAS_DSHOW=1" + ac_dshow_cflags="-DPJMEDIA_HAS_VIDEO=1 -DPJMEDIA_VIDEO_DEV_HAS_DSHOW=1" - ac_dshow_ldflags=" -lstdc++ -lquartz -lole32 -loleaut32 -lrpcrt4 -lwinmm -luuid -lmingwex -lstrmiids " - LIBS="$LIBS -lstdc++ -lquartz -lole32 -loleaut32 -lrpcrt4 -lwinmm -luuid -lmingwex -lstrmiids " - fi - ;; - *darwin*) - ac_pjmedia_video=darwin_os + ac_dshow_ldflags=" -lstdc++ -lquartz -lole32 -loleaut32 -lrpcrt4 -lwinmm -luuid -lmingwex -lstrmiids " + LIBS="$LIBS -lstdc++ -lquartz -lole32 -loleaut32 -lrpcrt4 -lwinmm -luuid -lmingwex -lstrmiids " + fi + ;; + *darwin*) + ac_pjmedia_video=darwin_os - SAVED_LIBS="$LIBS" - LIBS="-framework AVFoundation -framework CoreGraphics -framework QuartzCore -framework CoreVideo -framework CoreMedia" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + SAVED_LIBS="$LIBS" + + LIBS="-framework AVFoundation -framework CoreGraphics -framework QuartzCore -framework CoreVideo -framework CoreMedia" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -7505,11 +7667,13 @@ then : ac_pjmedia_video_has_darwin=yes else $as_nop ac_pjmedia_video_has_darwin=no + fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - LIBS="-framework VideoToolbox" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + + LIBS="-framework VideoToolbox" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -7528,8 +7692,9 @@ else $as_nop fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - LIBS="-framework Metal" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + + LIBS="-framework Metal" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -7548,8 +7713,9 @@ else $as_nop fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - LIBS="-framework OpenGLES" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + + LIBS="-framework OpenGLES" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -7568,51 +7734,56 @@ else $as_nop fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - LIBS="$SAVED_LIBS" - if test "$ac_pjmedia_video_has_darwin" = "yes"; then - ac_darwin_cflags="-DPJMEDIA_VIDEO_DEV_HAS_DARWIN=1" - LIBS="$LIBS -framework AVFoundation -framework CoreGraphics -framework QuartzCore -framework CoreVideo -framework CoreMedia" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if AVFoundation framework is available... yes" >&5 + + LIBS="$SAVED_LIBS" + if test "$ac_pjmedia_video_has_darwin" = "yes"; then + ac_darwin_cflags="-DPJMEDIA_VIDEO_DEV_HAS_DARWIN=1" + LIBS="$LIBS -framework AVFoundation -framework CoreGraphics -framework QuartzCore -framework CoreVideo -framework CoreMedia" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if AVFoundation framework is available... yes" >&5 printf "%s\n" "Checking if AVFoundation framework is available... yes" >&6; } - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if AVFoundation framework is available... no" >&5 + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if AVFoundation framework is available... no" >&5 printf "%s\n" "Checking if AVFoundation framework is available... no" >&6; } - fi - if test "$ac_pjmedia_video_has_metal" = "yes"; then - ac_darwin_cflags+=" -DPJMEDIA_VIDEO_DEV_HAS_METAL=1" - LIBS="$LIBS -framework Metal -framework MetalKit" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Metal framework is available... yes" >&5 + fi + + if test "$ac_pjmedia_video_has_metal" = "yes"; then + ac_darwin_cflags+=" -DPJMEDIA_VIDEO_DEV_HAS_METAL=1" + LIBS="$LIBS -framework Metal -framework MetalKit" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Metal framework is available... yes" >&5 printf "%s\n" "Checking if Metal framework is available... yes" >&6; } - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Metal framework is available... no" >&5 + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Metal framework is available... no" >&5 printf "%s\n" "Checking if Metal framework is available... no" >&6; } - fi - if test "$ac_pjmedia_video_has_vtoolbox" = "yes"; then - #ac_darwin_cflags+=" -DPJMEDIA_HAS_VID_TOOLBOX_CODEC=1" - LIBS="$LIBS -framework VideoToolbox" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if VideoToolbox framework is available... yes" >&5 + fi + + if test "$ac_pjmedia_video_has_vtoolbox" = "yes"; then + #ac_darwin_cflags+=" -DPJMEDIA_HAS_VID_TOOLBOX_CODEC=1" + LIBS="$LIBS -framework VideoToolbox" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if VideoToolbox framework is available... yes" >&5 printf "%s\n" "Checking if VideoToolbox framework is available... yes" >&6; } - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if VideoToolbox framework is available... no" >&5 + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if VideoToolbox framework is available... no" >&5 printf "%s\n" "Checking if VideoToolbox framework is available... no" >&6; } - fi - if test "$ac_pjmedia_video_has_ios_opengl" = "yes"; then - ac_darwin_cflags+=" -DPJMEDIA_VIDEO_DEV_HAS_IOS_OPENGL=1" - LIBS="$LIBS -framework OpenGLES" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if OpenGLES framework is available... yes" >&5 + fi + + if test "$ac_pjmedia_video_has_ios_opengl" = "yes"; then + ac_darwin_cflags+=" -DPJMEDIA_VIDEO_DEV_HAS_IOS_OPENGL=1" + LIBS="$LIBS -framework OpenGLES" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if OpenGLES framework is available... yes" >&5 printf "%s\n" "Checking if OpenGLES framework is available... yes" >&6; } - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if OpenGLES framework is available... no" >&5 + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if OpenGLES framework is available... no" >&5 printf "%s\n" "Checking if OpenGLES framework is available... no" >&6; } - fi - if false; then - # QTKit is deprecated, see ticket #1931. - ac_pjmedia_video=mac_os + fi + + if false; then + # QTKit is deprecated, see ticket #1931. + ac_pjmedia_video=mac_os - SAVED_LIBS="$LIBS" - LIBS="-framework QTKit" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + SAVED_LIBS="$LIBS" + LIBS="-framework QTKit" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -7631,29 +7802,31 @@ else $as_nop fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - LIBS="$SAVED_LIBS" - if test "$ac_pjmedia_video_has_qt" = "yes"; then - ac_qt_cflags="-DPJMEDIA_VIDEO_DEV_HAS_QT=1" - LIBS="$LIBS -framework QTKit -framework QuartzCore -framework OpenGL" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if QTKit framework is available... yes" >&5 + LIBS="$SAVED_LIBS" + if test "$ac_pjmedia_video_has_qt" = "yes"; then + ac_qt_cflags="-DPJMEDIA_VIDEO_DEV_HAS_QT=1" + LIBS="$LIBS -framework QTKit -framework QuartzCore -framework OpenGL" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if QTKit framework is available... yes" >&5 printf "%s\n" "Checking if QTKit framework is available... yes" >&6; } - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if QTKit framework is available... no" >&5 + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if QTKit framework is available... no" >&5 printf "%s\n" "Checking if QTKit framework is available... no" >&6; } - fi - fi - ;; - esac + fi + fi + ;; + esac fi # Check whether --enable-ext_sound was given. if test ${enable_ext_sound+y} then : - enableval=$enable_ext_sound; if test "$enable_ext_sound" = "yes"; then - ac_pjmedia_snd=external - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if external sound is set... yes" >&5 + enableval=$enable_ext_sound; + if test "$enable_ext_sound" = "yes"; then + ac_pjmedia_snd=external + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if external sound is set... yes" >&5 printf "%s\n" "Checking if external sound is set... yes" >&6; } - fi + fi + fi @@ -7662,14 +7835,17 @@ fi # Check whether --enable-small-filter was given. if test ${enable_small_filter+y} then : - enableval=$enable_small_filter; if test "$enable_small_filter" = "no"; then - ac_no_small_filter='-DPJMEDIA_HAS_SMALL_FILTER=0' - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if small filter is disabled... yes" >&5 + enableval=$enable_small_filter; + if test "$enable_small_filter" = "no"; then + ac_no_small_filter='-DPJMEDIA_HAS_SMALL_FILTER=0' + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if small filter is disabled... yes" >&5 printf "%s\n" "Checking if small filter is disabled... yes" >&6; } - fi + fi + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if small filter is disabled... no" >&5 printf "%s\n" "Checking if small filter is disabled... no" >&6; } + fi @@ -7677,14 +7853,17 @@ fi # Check whether --enable-large-filter was given. if test ${enable_large_filter+y} then : - enableval=$enable_large_filter; if test "$enable_large_filter" = "no"; then - ac_no_large_filter='-DPJMEDIA_HAS_LARGE_FILTER=0' - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if large filter is disabled... yes" >&5 + enableval=$enable_large_filter; + if test "$enable_large_filter" = "no"; then + ac_no_large_filter='-DPJMEDIA_HAS_LARGE_FILTER=0' + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if large filter is disabled... yes" >&5 printf "%s\n" "Checking if large filter is disabled... yes" >&6; } - fi + fi + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if large filter is disabled... no" >&5 printf "%s\n" "Checking if large filter is disabled... no" >&6; } + fi @@ -7692,14 +7871,17 @@ fi # Check whether --enable-speex-aec was given. if test ${enable_speex_aec+y} then : - enableval=$enable_speex_aec; if test "$enable_speex_aec" = "no"; then - ac_no_speex_aec='-DPJMEDIA_HAS_SPEEX_AEC=0' - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex AEC is disabled...yes" >&5 + enableval=$enable_speex_aec; + if test "$enable_speex_aec" = "no"; then + ac_no_speex_aec='-DPJMEDIA_HAS_SPEEX_AEC=0' + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex AEC is disabled...yes" >&5 printf "%s\n" "Checking if Speex AEC is disabled...yes" >&6; } - fi + fi + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex AEC is disabled...no" >&5 printf "%s\n" "Checking if Speex AEC is disabled...no" >&6; } + fi @@ -7707,16 +7889,19 @@ fi # Check whether --enable-g711-codec was given. if test ${enable_g711_codec+y} then : - enableval=$enable_g711_codec; if test "$enable_g711_codec" = "no"; then - ac_no_g711_codec=1 - printf "%s\n" "#define PJMEDIA_HAS_G711_CODEC 0" >>confdefs.h + enableval=$enable_g711_codec; + if test "$enable_g711_codec" = "no"; then + ac_no_g711_codec=1 + printf "%s\n" "#define PJMEDIA_HAS_G711_CODEC 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if G.711 codec is disabled...yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if G.711 codec is disabled...yes" >&5 printf "%s\n" "Checking if G.711 codec is disabled...yes" >&6; } - fi + fi + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if G.711 codec is disabled...no" >&5 printf "%s\n" "Checking if G.711 codec is disabled...no" >&6; } + fi @@ -7725,34 +7910,39 @@ fi # Check whether --enable-l16-codec was given. if test ${enable_l16_codec+y} then : - enableval=$enable_l16_codec; if test "$enable_l16_codec" = "no"; then - ac_no_l16_codec=1 - printf "%s\n" "#define PJMEDIA_HAS_L16_CODEC 0" >>confdefs.h + enableval=$enable_l16_codec; + if test "$enable_l16_codec" = "no"; then + ac_no_l16_codec=1 + printf "%s\n" "#define PJMEDIA_HAS_L16_CODEC 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if L16 codecs are disabled...yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if L16 codecs are disabled...yes" >&5 printf "%s\n" "Checking if L16 codecs are disabled...yes" >&6; } - fi + fi + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if L16 codec is disabled...no" >&5 printf "%s\n" "Checking if L16 codec is disabled...no" >&6; } -fi +fi # Check whether --enable-gsm-codec was given. if test ${enable_gsm_codec+y} then : - enableval=$enable_gsm_codec; if test "$enable_gsm_codec" = "no"; then - ac_no_gsm_codec=1 - printf "%s\n" "#define PJMEDIA_HAS_GSM_CODEC 0" >>confdefs.h + enableval=$enable_gsm_codec; + if test "$enable_gsm_codec" = "no"; then + ac_no_gsm_codec=1 + printf "%s\n" "#define PJMEDIA_HAS_GSM_CODEC 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if GSM codec is disabled...yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if GSM codec is disabled...yes" >&5 printf "%s\n" "Checking if GSM codec is disabled...yes" >&6; } - fi + fi + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if GSM codec is disabled...no" >&5 printf "%s\n" "Checking if GSM codec is disabled...no" >&6; } + fi @@ -7760,16 +7950,19 @@ fi # Check whether --enable-g722-codec was given. if test ${enable_g722_codec+y} then : - enableval=$enable_g722_codec; if test "$enable_g722_codec" = "no"; then - ac_no_g722_codec=1 - printf "%s\n" "#define PJMEDIA_HAS_G722_CODEC 0" >>confdefs.h + enableval=$enable_g722_codec; + if test "$enable_g722_codec" = "no"; then + ac_no_g722_codec=1 + printf "%s\n" "#define PJMEDIA_HAS_G722_CODEC 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if G.722 codec is disabled...yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if G.722 codec is disabled...yes" >&5 printf "%s\n" "Checking if G.722 codec is disabled...yes" >&6; } - fi + fi + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if G.722 codec is disabled...no" >&5 printf "%s\n" "Checking if G.722 codec is disabled...no" >&6; } + fi @@ -7777,16 +7970,19 @@ fi # Check whether --enable-g7221-codec was given. if test ${enable_g7221_codec+y} then : - enableval=$enable_g7221_codec; if test "$enable_g7221_codec" = "no"; then - ac_no_g7221_codec=1 - printf "%s\n" "#define PJMEDIA_HAS_G7221_CODEC 0" >>confdefs.h + enableval=$enable_g7221_codec; + if test "$enable_g7221_codec" = "no"; then + ac_no_g7221_codec=1 + printf "%s\n" "#define PJMEDIA_HAS_G7221_CODEC 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if G.722.1 codec is disabled...yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if G.722.1 codec is disabled...yes" >&5 printf "%s\n" "Checking if G.722.1 codec is disabled...yes" >&6; } - fi + fi + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if G.722.1 codec is disabled...no" >&5 printf "%s\n" "Checking if G.722.1 codec is disabled...no" >&6; } + fi @@ -7794,16 +7990,19 @@ fi # Check whether --enable-speex-codec was given. if test ${enable_speex_codec+y} then : - enableval=$enable_speex_codec; if test "$enable_speex_codec" = "no"; then - ac_no_speex_codec=1 - printf "%s\n" "#define PJMEDIA_HAS_SPEEX_CODEC 0" >>confdefs.h + enableval=$enable_speex_codec; + if test "$enable_speex_codec" = "no"; then + ac_no_speex_codec=1 + printf "%s\n" "#define PJMEDIA_HAS_SPEEX_CODEC 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex codec is disabled...yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex codec is disabled...yes" >&5 printf "%s\n" "Checking if Speex codec is disabled...yes" >&6; } - fi + fi + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex codec is disabled...no" >&5 printf "%s\n" "Checking if Speex codec is disabled...no" >&6; } + fi @@ -7811,16 +8010,19 @@ fi # Check whether --enable-ilbc-codec was given. if test ${enable_ilbc_codec+y} then : - enableval=$enable_ilbc_codec; if test "$enable_ilbc_codec" = "no"; then - ac_no_ilbc_codec=1 - printf "%s\n" "#define PJMEDIA_HAS_ILBC_CODEC 0" >>confdefs.h + enableval=$enable_ilbc_codec; + if test "$enable_ilbc_codec" = "no"; then + ac_no_ilbc_codec=1 + printf "%s\n" "#define PJMEDIA_HAS_ILBC_CODEC 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if iLBC codec is disabled...yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if iLBC codec is disabled...yes" >&5 printf "%s\n" "Checking if iLBC codec is disabled...yes" >&6; } - fi + fi + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if iLBC codec is disabled...no" >&5 printf "%s\n" "Checking if iLBC codec is disabled...no" >&6; } + fi @@ -7828,10 +8030,10 @@ fi if test ${enable_libsamplerate+y} then : enableval=$enable_libsamplerate; - if test "$enable_libsamplerate" = "yes"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libsamplerate is enabled...yes" >&5 + if test "$enable_libsamplerate" = "yes"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libsamplerate is enabled...yes" >&5 printf "%s\n" "Checking if libsamplerate is enabled...yes" >&6; } - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for src_new in -lsamplerate" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for src_new in -lsamplerate" >&5 printf %s "checking for src_new in -lsamplerate... " >&6; } if test ${ac_cv_lib_samplerate_src_new+y} then : @@ -7874,15 +8076,16 @@ then : fi - ac_pjmedia_resample=libsamplerate - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libsamplerate is enabled...no" >&5 + ac_pjmedia_resample=libsamplerate + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libsamplerate is enabled...no" >&5 printf "%s\n" "Checking if libsamplerate is enabled...no" >&6; } - fi + fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libsamplerate is enabled...no" >&5 printf "%s\n" "Checking if libsamplerate is enabled...no" >&6; } + fi @@ -7890,11 +8093,13 @@ fi # Check whether --enable-resample_dll was given. if test ${enable_resample_dll+y} then : - enableval=$enable_resample_dll; if test "$enable_resample_dll" = "yes"; then - ac_resample_dll=1 - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Building libresample as shared library... yes" >&5 + enableval=$enable_resample_dll; + if test "$enable_resample_dll" = "yes"; then + ac_resample_dll=1 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Building libresample as shared library... yes" >&5 printf "%s\n" "Building libresample as shared library... yes" >&6; } - fi + fi + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Building libresample as shared library... no" >&5 printf "%s\n" "Building libresample as shared library... no" >&6; } @@ -7906,18 +8111,19 @@ fi if test ${enable_speex_resample+y} then : enableval=$enable_speex_resample; - if test "$enable_speex_resample" = "yes"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex resample is enabled... yes" >&5 + if test "$enable_speex_resample" = "yes"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex resample is enabled... yes" >&5 printf "%s\n" "Checking if Speex resample is enabled... yes" >&6; } - ac_pjmedia_resample=speex - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex resample is enabled... no" >&5 + ac_pjmedia_resample=speex + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex resample is enabled... no" >&5 printf "%s\n" "Checking if Speex resample is enabled... no" >&6; } - fi + fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex resample is enabled... no" >&5 printf "%s\n" "Checking if Speex resample is enabled... no" >&6; } + fi @@ -7933,24 +8139,24 @@ fi if test "x$ac_cross_compile" != "x" -a "x$with_sdl" = "xno"; then - enable_sdl=no + nable_sdl=no fi # Check whether --enable-sdl was given. if test ${enable_sdl+y} then : enableval=$enable_sdl; - if test "$enable_sdl" = "no"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if SDL is disabled... yes" >&5 + if test "$enable_sdl" = "no"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if SDL is disabled... yes" >&5 printf "%s\n" "Checking if SDL is disabled... yes" >&6; } - fi + fi else $as_nop - if test "x$with_sdl" != "xno" -a "x$with_sdl" != "x"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using SDL prefix... $with_sdl" >&5 + if test "x$with_sdl" != "xno" -a "x$with_sdl" != "x"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using SDL prefix... $with_sdl" >&5 printf "%s\n" "Using SDL prefix... $with_sdl" >&6; } - for ac_prog in sdl2-config sdl-config + for ac_prog in sdl2-config sdl-config do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 @@ -8000,8 +8206,8 @@ fi test -n "$SDL_CONFIG" && break done - else - for ac_prog in sdl2-config sdl-config + else + for ac_prog in sdl2-config sdl-config do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 @@ -8051,28 +8257,28 @@ fi test -n "$SDL_CONFIG" && break done - fi + fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking SDL availability" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking SDL availability" >&5 printf %s "checking SDL availability... " >&6; } - if test "x$SDL_CONFIG" = "x"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5 + if test "x$SDL_CONFIG" = "x"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5 printf "%s\n" "not found" >&6; } - elif (sh -c "$SDL_CONFIG --version" | grep -e '^1\.3' -e '^2\.') then + elif (sh -c "$SDL_CONFIG --version" | grep -e '^1\.3' -e '^2\.') then - ac_sdl_cflags=`$SDL_CONFIG --cflags` - ac_sdl_cflags="-DPJMEDIA_VIDEO_DEV_HAS_SDL=1 $ac_sdl_cflags" - ac_sdl_ldflags=`$SDL_CONFIG --libs` - ac_sdl_ldflags=`echo "${ac_sdl_ldflags}" | sed -e 's/-mwindows//g'` - LIBS="$LIBS $ac_sdl_ldflags" - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Unsupported SDL version" >&5 + ac_sdl_cflags=`$SDL_CONFIG --cflags` + ac_sdl_cflags="-DPJMEDIA_VIDEO_DEV_HAS_SDL=1 $ac_sdl_cflags" + ac_sdl_ldflags=`$SDL_CONFIG --libs` + ac_sdl_ldflags=`echo "${ac_sdl_ldflags}" | sed -e 's/-mwindows//g'` + LIBS="$LIBS $ac_sdl_ldflags" + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Unsupported SDL version" >&5 printf "%s\n" "Unsupported SDL version" >&6; } - fi + fi -fi +fi @@ -8094,29 +8300,29 @@ fi if test ${enable_ffmpeg+y} then : enableval=$enable_ffmpeg; - ac_has_ffmpeg=0 + ac_has_ffmpeg=0 - if test "$enable_ffmpeg" = "no"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if ffmpeg is disabled... yes" >&5 + if test "$enable_ffmpeg" = "no"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if ffmpeg is disabled... yes" >&5 printf "%s\n" "Checking if ffmpeg is disabled... yes" >&6; } - fi + fi else $as_nop - FFMPEG_PREFIX="" + FFMPEG_PREFIX="" - SAVED_PKG_CONFIG_PATH=$PKG_CONFIG_PATH - if test "x$with_ffmpeg" != "xno" -a "x$with_ffmpeg" != "x"; then - FFMPEG_PREFIX=$with_ffmpeg - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using ffmpeg prefix... $FFMPEG_PREFIX" >&5 + SAVED_PKG_CONFIG_PATH=$PKG_CONFIG_PATH + if test "x$with_ffmpeg" != "xno" -a "x$with_ffmpeg" != "x"; then + FFMPEG_PREFIX=$with_ffmpeg + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using ffmpeg prefix... $FFMPEG_PREFIX" >&5 printf "%s\n" "Using ffmpeg prefix... $FFMPEG_PREFIX" >&6; } - export PKG_CONFIG_PATH=$FFMPEG_PREFIX/lib/pkgconfig - fi + export PKG_CONFIG_PATH=$FFMPEG_PREFIX/lib/pkgconfig + fi - for ac_prog in pkg-config "python pkgconfig.py" + for ac_prog in pkg-config "python pkgconfig.py" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 @@ -8165,52 +8371,52 @@ done test -n "$PKG_CONFIG" || PKG_CONFIG="none" - if test "$PKG_CONFIG" != "none"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking ffmpeg packages" >&5 + if test "$PKG_CONFIG" != "none"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking ffmpeg packages" >&5 printf %s "checking ffmpeg packages... " >&6; } - av_pkg="" - if $PKG_CONFIG --exists libavdevice; then - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVDEVICE=1" - av_pkg="$av_pkg libavdevice" - fi - if $PKG_CONFIG --exists libavformat; then - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVFORMAT=1" - av_pkg="$av_pkg libavformat" - fi - if $PKG_CONFIG --exists libavcodec; then - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCODEC=1" - av_pkg="$av_pkg libavcodec" - fi - if $PKG_CONFIG --exists libswscale; then - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBSWSCALE=1" - av_pkg="$av_pkg libswscale" - fi - if $PKG_CONFIG --exists libavutil; then - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVUTIL=1" - av_pkg="$av_pkg libavutil" - fi - - if test "x$av_pkg" = "x"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none detected (check the prefix)! **" >&5 + av_pkg="" + if $PKG_CONFIG --exists libavdevice; then + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVDEVICE=1" + av_pkg="$av_pkg libavdevice" + fi + if $PKG_CONFIG --exists libavformat; then + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVFORMAT=1" + av_pkg="$av_pkg libavformat" + fi + if $PKG_CONFIG --exists libavcodec; then + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCODEC=1" + av_pkg="$av_pkg libavcodec" + fi + if $PKG_CONFIG --exists libswscale; then + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBSWSCALE=1" + av_pkg="$av_pkg libswscale" + fi + if $PKG_CONFIG --exists libavutil; then + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVUTIL=1" + av_pkg="$av_pkg libavutil" + fi + + if test "x$av_pkg" = "x"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none detected (check the prefix)! **" >&5 printf "%s\n" "none detected (check the prefix)! **" >&6; } - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $av_pkg" >&5 + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $av_pkg" >&5 printf "%s\n" "$av_pkg" >&6; } - fi + fi - ac_ffmpeg_cflags="$ac_ffmpeg_cflags `$PKG_CONFIG --cflags $av_pkg`" - ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags `$PKG_CONFIG --libs $av_pkg`" + ac_ffmpeg_cflags="$ac_ffmpeg_cflags `$PKG_CONFIG --cflags $av_pkg`" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags `$PKG_CONFIG --libs $av_pkg`" - else + else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: *** Warning: neither pkg-config nor python is available, ffmpeg dependency cannot be calculated. If ffmpeg libraries are not detected, you need to specify the correct CFLAGS and LDFLAGS settings for ffmpeg prior to invoking configure ***" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: *** Warning: neither pkg-config nor python is available, ffmpeg dependency cannot be calculated. If ffmpeg libraries are not detected, you need to specify the correct CFLAGS and LDFLAGS settings for ffmpeg prior to invoking configure ***" >&5 printf "%s\n" "*** Warning: neither pkg-config nor python is available, ffmpeg dependency cannot be calculated. If ffmpeg libraries are not detected, you need to specify the correct CFLAGS and LDFLAGS settings for ffmpeg prior to invoking configure ***" >&6; } - LIBS="-L$FFMPEG_PREFIX/lib $LIBS" - LDFLAGS="-L$FFMPEG_PREFIX/lib $LDFLAGS" - CFLAGS="-I$FFMPEG_PREFIX/include $CFLAGS" + LIBS="-L$FFMPEG_PREFIX/lib $LIBS" + LDFLAGS="-L$FFMPEG_PREFIX/lib $LDFLAGS" + CFLAGS="-I$FFMPEG_PREFIX/include $CFLAGS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for avdevice_version in -lavdevice" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for avdevice_version in -lavdevice" >&5 printf %s "checking for avdevice_version in -lavdevice... " >&6; } if test ${ac_cv_lib_avdevice_avdevice_version+y} then : @@ -8247,13 +8453,13 @@ fi printf "%s\n" "$ac_cv_lib_avdevice_avdevice_version" >&6; } if test "x$ac_cv_lib_avdevice_avdevice_version" = xyes then : - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVDEVICE=1" - ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavdevice" - + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVDEVICE=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavdevice" fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for av_malloc in -lavutil" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for av_malloc in -lavutil" >&5 printf %s "checking for av_malloc in -lavutil... " >&6; } if test ${ac_cv_lib_avutil_av_malloc+y} then : @@ -8290,21 +8496,20 @@ fi printf "%s\n" "$ac_cv_lib_avutil_av_malloc" >&6; } if test "x$ac_cv_lib_avutil_av_malloc" = xyes then : - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVUTIL=1" - ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavutil" - + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVUTIL=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavutil" fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for avcodec_init in -lavcodec" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for avcodec_init in -lavcodec" >&5 printf %s "checking for avcodec_init in -lavcodec... " >&6; } if test ${ac_cv_lib_avcodec_avcodec_init+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS -LIBS="-lavcodec -lavutil - $LIBS" +LIBS="-lavcodec -lavutil $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -8334,20 +8539,20 @@ fi printf "%s\n" "$ac_cv_lib_avcodec_avcodec_init" >&6; } if test "x$ac_cv_lib_avcodec_avcodec_init" = xyes then : - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCODEC=1" - ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavcodec" + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCODEC=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavcodec" fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for av_register_all in -lavformat" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for av_register_all in -lavformat" >&5 printf %s "checking for av_register_all in -lavformat... " >&6; } if test ${ac_cv_lib_avformat_av_register_all+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS -LIBS="-lavformat -lavcodec -lavutil - $LIBS" +LIBS="-lavformat -lavcodec -lavutil $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -8377,20 +8582,20 @@ fi printf "%s\n" "$ac_cv_lib_avformat_av_register_all" >&6; } if test "x$ac_cv_lib_avformat_av_register_all" = xyes then : - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVFORMAT=1" - ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavformat" + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVFORMAT=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavformat" fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sws_scale in -lswscale" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sws_scale in -lswscale" >&5 printf %s "checking for sws_scale in -lswscale... " >&6; } if test ${ac_cv_lib_swscale_sws_scale+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS -LIBS="-lswscale -lavutil - $LIBS" +LIBS="-lswscale -lavutil $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -8420,12 +8625,13 @@ fi printf "%s\n" "$ac_cv_lib_swscale_sws_scale" >&6; } if test "x$ac_cv_lib_swscale_sws_scale" = xyes then : - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBSWSCALE=1" - ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lswscale" + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBSWSCALE=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lswscale" fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for avcore_version in -lavcore" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for avcore_version in -lavcore" >&5 printf %s "checking for avcore_version in -lavcore... " >&6; } if test ${ac_cv_lib_avcore_avcore_version+y} then : @@ -8462,17 +8668,14 @@ fi printf "%s\n" "$ac_cv_lib_avcore_avcore_version" >&6; } if test "x$ac_cv_lib_avcore_avcore_version" = xyes then : - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCORE=1" - ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavcore" - + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCORE=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavcore" fi + fi - fi - - ac_fn_c_check_type "$LINENO" "enum AVPixelFormat" "ac_cv_type_enum_AVPixelFormat" "#include - + ac_fn_c_check_type "$LINENO" "enum AVPixelFormat" "ac_cv_type_enum_AVPixelFormat" "#include " if test "x$ac_cv_type_enum_AVPixelFormat" = xyes then : @@ -8481,12 +8684,13 @@ printf "%s\n" "#define HAVE_ENUM_AVPIXELFORMAT 1" >>confdefs.h else $as_nop - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_USE_OLD_FFMPEG=1" + + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_USE_OLD_FFMPEG=1" fi - LIBS="$LIBS $ac_ffmpeg_ldflags" - export PKG_CONFIG_PATH=$SAVED_PKG_CONFIG_PATH + LIBS="$LIBS $ac_ffmpeg_ldflags" + export PKG_CONFIG_PATH=$SAVED_PKG_CONFIG_PATH fi @@ -8496,16 +8700,16 @@ fi if test ${enable_v4l2+y} then : enableval=$enable_v4l2; - if test "$enable_v4l2" = "no"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if V4L2 is disabled... yes" >&5 + if test "$enable_v4l2" = "no"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if V4L2 is disabled... yes" >&5 printf "%s\n" "Checking if V4L2 is disabled... yes" >&6; } - fi + fi else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for v4l2_open in -lv4l2" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for v4l2_open in -lv4l2" >&5 printf %s "checking for v4l2_open in -lv4l2... " >&6; } if test ${ac_cv_lib_v4l2_v4l2_open+y} then : @@ -8542,14 +8746,14 @@ fi printf "%s\n" "$ac_cv_lib_v4l2_v4l2_open" >&6; } if test "x$ac_cv_lib_v4l2_v4l2_open" = xyes then : - ac_v4l2_cflags="-DPJMEDIA_VIDEO_DEV_HAS_V4L2=1" - ac_v4l2_ldflags="-lv4l2" - LIBS="$LIBS -lv4l2" - + ac_v4l2_cflags="-DPJMEDIA_VIDEO_DEV_HAS_V4L2=1" + ac_v4l2_ldflags="-lv4l2" + LIBS="$LIBS -lv4l2" fi + fi @@ -8574,66 +8778,69 @@ fi if test ${enable_openh264+y} then : enableval=$enable_openh264; - if test "$enable_openh264" = "no"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if OpenH264 is disabled... yes" >&5 + if test "$enable_openh264" = "no"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if OpenH264 is disabled... yes" >&5 printf "%s\n" "Checking if OpenH264 is disabled... yes" >&6; } - fi + fi else $as_nop - if test "x$with_openh264" != "xno" -a "x$with_openh264" != "x"; then - OPENH264_PREFIX=$with_openh264 - OPENH264_CFLAGS="-I$OPENH264_PREFIX/include" - OPENH264_LDFLAGS="-L$OPENH264_PREFIX/lib" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using OpenH264 prefix... $with_openh264" >&5 + if test "x$with_openh264" != "xno" -a "x$with_openh264" != "x"; then + OPENH264_PREFIX=$with_openh264 + OPENH264_CFLAGS="-I$OPENH264_PREFIX/include" + OPENH264_LDFLAGS="-L$OPENH264_PREFIX/lib" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using OpenH264 prefix... $with_openh264" >&5 printf "%s\n" "Using OpenH264 prefix... $with_openh264" >&6; } - else - OPENH264_CFLAGS="" - OPENH264_LDFLAGS="" - fi + else + OPENH264_CFLAGS="" + OPENH264_LDFLAGS="" + fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking OpenH264 usability" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking OpenH264 usability" >&5 printf %s "checking OpenH264 usability... " >&6; } - OPENH264_LIBS="-lopenh264 -lstdc++" + OPENH264_LIBS="-lopenh264 -lstdc++" - SAVED_LIBS="$LIBS" - SAVED_LDFLAGS="$LDFLAGS" - SAVED_CFLAGS="$CFLAGS" + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CFLAGS="$CFLAGS" - LIBS="$OPENH264_LIBS $LIBS" - LDFLAGS="$OPENH264_LDFLAGS $LDFLAGS" - CFLAGS="$OPENH264_CFLAGS $CFLAGS" + LIBS="$OPENH264_LIBS $LIBS" + LDFLAGS="$OPENH264_LDFLAGS $LDFLAGS" + CFLAGS="$OPENH264_CFLAGS $CFLAGS" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include - #include + + #include + #include int main (void) { WelsCreateSVCEncoder(0); - ; return 0; } + _ACEOF if ac_fn_c_try_link "$LINENO" then : - ac_openh264_cflags="-DPJMEDIA_HAS_OPENH264_CODEC=1 $OPENH264_CFLAGS" - ac_openh264_ldflags="$OPENH264_LDFLAGS $OPENH264_LIBS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + + ac_openh264_cflags="-DPJMEDIA_HAS_OPENH264_CODEC=1 $OPENH264_CFLAGS" + ac_openh264_ldflags="$OPENH264_LDFLAGS $OPENH264_LIBS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop - LIBS="$SAVED_LIBS" - LDFLAGS="$SAVED_LDFLAGS" - CFLAGS="$SAVED_CFLAGS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CFLAGS="$SAVED_CFLAGS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -8663,66 +8870,69 @@ fi if test ${enable_vpx+y} then : enableval=$enable_vpx; - if test "$enable_vpx" = "no"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if VPX is disabled... yes" >&5 + if test "$enable_vpx" = "no"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if VPX is disabled... yes" >&5 printf "%s\n" "Checking if VPX is disabled... yes" >&6; } - fi + fi else $as_nop - if test "x$with_vpx" != "xno" -a "x$with_vpx" != "x"; then - VPX_PREFIX=$with_vpx - VPX_CFLAGS="-I$VPX_PREFIX/include" - VPX_LDFLAGS="-L$VPX_PREFIX/lib" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using VPX prefix... $with_vpx" >&5 + if test "x$with_vpx" != "xno" -a "x$with_vpx" != "x"; then + VPX_PREFIX=$with_vpx + VPX_CFLAGS="-I$VPX_PREFIX/include" + VPX_LDFLAGS="-L$VPX_PREFIX/lib" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using VPX prefix... $with_vpx" >&5 printf "%s\n" "Using VPX prefix... $with_vpx" >&6; } - else - VPX_CFLAGS="" - VPX_LDFLAGS="" - fi + else + VPX_CFLAGS="" + VPX_LDFLAGS="" + fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking VPX usability" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking VPX usability" >&5 printf %s "checking VPX usability... " >&6; } - VPX_LIBS="-lvpx" + VPX_LIBS="-lvpx" - SAVED_LIBS="$LIBS" - SAVED_LDFLAGS="$LDFLAGS" - SAVED_CFLAGS="$CFLAGS" + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CFLAGS="$CFLAGS" - LIBS="$VPX_LIBS $LIBS" - LDFLAGS="$VPX_LDFLAGS $LDFLAGS" - CFLAGS="$VPX_CFLAGS $CFLAGS" + LIBS="$VPX_LIBS $LIBS" + LDFLAGS="$VPX_LDFLAGS $LDFLAGS" + CFLAGS="$VPX_CFLAGS $CFLAGS" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include - #include + + #include + #include int main (void) { vpx_codec_iface_t *(*enc_if)() = &vpx_codec_vp8_cx; - ; return 0; } + _ACEOF if ac_fn_c_try_link "$LINENO" then : - ac_vpx_cflags="-DPJMEDIA_HAS_VPX_CODEC=1 $VPX_CFLAGS" - ac_vpx_ldflags="$VPX_LDFLAGS $VPX_LIBS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + + ac_vpx_cflags="-DPJMEDIA_HAS_VPX_CODEC=1 $VPX_CFLAGS" + ac_vpx_ldflags="$VPX_LDFLAGS $VPX_LIBS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop - LIBS="$SAVED_LIBS" - LDFLAGS="$SAVED_LDFLAGS" - CFLAGS="$SAVED_CFLAGS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CFLAGS="$SAVED_CFLAGS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -8731,7 +8941,6 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \ fi - # Check whether --enable-ipp was given. if test ${enable_ipp+y} then : @@ -8780,96 +8989,95 @@ if test "x$enable_ipp" != "xno"; then printf %s "checking Intel IPP location... " >&6; } if test "x$with_ipp" != "xno" -a "x$with_ipp" != "x"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_ipp" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_ipp" >&5 printf "%s\n" "$with_ipp" >&6; } - IPPROOT=$with_ipp + IPPROOT=$with_ipp elif test "x$IPPROOT" = "x"; then - if test -d /opt/intel/ipp; then - IPPROOT=`ls -d /opt/intel/ipp/*/* | head -1` - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: autodetected in $IPPROOT" >&5 + if test -d /opt/intel/ipp; then + IPPROOT=`ls -d /opt/intel/ipp/*/* | head -1` + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: autodetected in $IPPROOT" >&5 printf "%s\n" "autodetected in $IPPROOT" >&6; } - fi + fi else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $IPPROOT" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $IPPROOT" >&5 printf "%s\n" "$IPPROOT" >&6; } fi if test "x$with_ipp_arch" != "xno"; then - IPP_SUFFIX=$with_ipp_arch - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: IPP arch suffix is set to $IPP_SUFFIX" >&5 + IPP_SUFFIX=$with_ipp_arch + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: IPP arch suffix is set to $IPP_SUFFIX" >&5 printf "%s\n" "IPP arch suffix is set to $IPP_SUFFIX" >&6; } else - IPP_SUFFIX="" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: IPP arch suffix is set to empty" >&5 + IPP_SUFFIX="" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: IPP arch suffix is set to empty" >&5 printf "%s\n" "IPP arch suffix is set to empty" >&6; } fi if test x$IPPROOT = x; then - as_fn_error $? "the location is neither specified nor can be guessed. Please specify with IPPROOT env var or with --with-ipp option" "$LINENO" 5 + as_fn_error $? "the location is neither specified nor can be guessed. Please specify with IPPROOT env var or with --with-ipp option" "$LINENO" 5 elif test ! -d $IPPROOT; then - as_fn_error $? "not found" "$LINENO" 5 + as_fn_error $? "not found" "$LINENO" 5 elif test ! -d $IPPROOT/include; then - as_fn_error $? "directory doesn't seem to be valid" "$LINENO" 5 + as_fn_error $? "directory doesn't seem to be valid" "$LINENO" 5 else - # IPP directory looks okay. - # Remove trailing backslash - IPPROOT=`echo $IPPROOT | sed 's/\/$//'` - - SAVED_CFLAGS="$CFLAGS" - SAVED_LDFLAGS="$LDFLAGS" - SAVED_LIBS="$LIBS" - - IPP_CFLAGS="-I$IPPROOT/include" - IPP_LIBS="-lippsc${IPP_SUFFIX} -lipps${IPP_SUFFIX} -lippcore${IPP_SUFFIX}" - -# - # Some differences between Mac OS X and Linux - case $target in - *darwin* ) - IPP_LDFLAGS="-L$IPPROOT/Libraries -L$IPPROOT/lib" - ;; - *) - # Find out where the libraries live. - IPP7_ARCH="" - if test -d $IPPROOT/lib/intel64; then - IPP7_ARCH="intel64" - elif test -d $IPPROOT/lib/ia32; then - IPP7_ARCH="ia32" - elif test -d $IPPROOT/lib/mic; then - IPP7_ARCH="mic" - fi - - if test -z "$IPP7_ARCH"; then - # IPP6 (and possibly below) - IPP_LDFLAGS="-L$IPPROOT/sharedlib" - IPP_LIBS="$IPP_LIBS -lippsr${IPP_SUFFIX} -lguide" - else - # IPP7 - if ! test -d $IPPROOT/../compiler; then - as_fn_error $? "Cannot find $IPPROOT/../compiler directory. Please set IPPROOT variable correctly" "$LINENO" 5 - fi - IPP_CFLAGS="$IPP_CFLAGS" - IPP_LDFLAGS="-L$IPPROOT/lib/intel64 -L$IPPROOT/../compiler/lib/$IPP7_ARCH" - IPP_LIBS="$IPP_LIBS -liomp5" - fi - ;; - esac + # IPP directory looks okay. + # Remove trailing backslash + IPPROOT=`echo $IPPROOT | sed 's/\/$//'` + + SAVED_CFLAGS="$CFLAGS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_LIBS="$LIBS" + + IPP_CFLAGS="-I$IPPROOT/include" + IPP_LIBS="-lippsc${IPP_SUFFIX} -lipps${IPP_SUFFIX} -lippcore${IPP_SUFFIX}" + + # Some differences between Mac OS X and Linux + case $target in + *darwin* ) + IPP_LDFLAGS="-L$IPPROOT/Libraries -L$IPPROOT/lib" + ;; + *) + # Find out where the libraries live. + IPP7_ARCH="" + if test -d $IPPROOT/lib/intel64; then + IPP7_ARCH="intel64" + elif test -d $IPPROOT/lib/ia32; then + IPP7_ARCH="ia32" + elif test -d $IPPROOT/lib/mic; then + IPP7_ARCH="mic" + fi - #IPP_LDFLAGS="-L$IPPROOT/sharedlib" - #Static: - #IPP_LIBS="-lippscmerged -lippsrmerged -lippsmerged -lippcore" + if test -z "$IPP7_ARCH"; then + # IPP6 (and possibly below) + IPP_LDFLAGS="-L$IPPROOT/sharedlib" + IPP_LIBS="$IPP_LIBS -lippsr${IPP_SUFFIX} -lguide" + else + # IPP7 + if ! test -d $IPPROOT/../compiler; then + as_fn_error $? "Cannot find $IPPROOT/../compiler directory. Please set IPPROOT variable correctly" "$LINENO" 5 + fi + IPP_CFLAGS="$IPP_CFLAGS" + IPP_LDFLAGS="-L$IPPROOT/lib/intel64 -L$IPPROOT/../compiler/lib/$IPP7_ARCH" + IPP_LIBS="$IPP_LIBS -liomp5" + fi + ;; + esac - CFLAGS="$CFLAGS $IPP_CFLAGS" - LDFLAGS="$LDFLAGS $IPP_LDFLAGS" - LIBS="$IPP_LIBS $LIBS" + #IPP_LDFLAGS="-L$IPPROOT/sharedlib" + #Static: + #IPP_LIBS="-lippscmerged -lippsrmerged -lippsmerged -lippcore" + CFLAGS="$CFLAGS $IPP_CFLAGS" + LDFLAGS="$LDFLAGS $IPP_LDFLAGS" + LIBS="$IPP_LIBS $LIBS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking Intel IPP usability" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking Intel IPP usability" >&5 printf %s "checking Intel IPP usability... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #include int main (void) { @@ -8877,6 +9085,7 @@ ippStaticInit(); ; return 0; } + _ACEOF if ac_fn_c_try_link "$LINENO" then : @@ -8887,117 +9096,119 @@ else $as_nop printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "Error: unable to recognize your IPP installation. Make sure the paths and ARCH suffix are set correctly, run with --help for more info See \`config.log' for more details" "$LINENO" 5; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - CFLAGS="$SAVED_CFLAGS" - LDFLAGS="$SAVED_LDFLAGS" - LIBS="$SAVED_LIBS" + CFLAGS="$SAVED_CFLAGS" + LDFLAGS="$SAVED_LDFLAGS" + LIBS="$SAVED_LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking Intel IPP samples location" >&5 printf %s "checking Intel IPP samples location... " >&6; } if test "x$with_ipp_samples" != "xno" -a "x$with_ipp_samples" != "x"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_ipp_samples" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_ipp_samples" >&5 printf "%s\n" "$with_ipp_samples" >&6; } - IPPSAMPLES=$with_ipp_samples + IPPSAMPLES=$with_ipp_samples elif test "x$IPPSAMPLES" = "x"; then - if test -d /opt/intel/ipp-samples; then - IPPSAMPLES=/opt/intel/ipp-samples - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: autodetected in $IPPSAMPLES" >&5 + if test -d /opt/intel/ipp-samples; then + IPPSAMPLES=/opt/intel/ipp-samples + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: autodetected in $IPPSAMPLES" >&5 printf "%s\n" "autodetected in $IPPSAMPLES" >&6; } - fi + fi else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $IPPSAMPLES" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $IPPSAMPLES" >&5 printf "%s\n" "$IPPSAMPLES" >&6; } fi if test x$IPPSAMPLES = x; then - as_fn_error $? "the location is neither specified nor can be guessed. Please specify with IPPSAMPLES env var or with --with-ipp-samples option" "$LINENO" 5 + as_fn_error $? "the location is neither specified nor can be guessed. Please specify with IPPSAMPLES env var or with --with-ipp-samples option" "$LINENO" 5 elif test ! -d $IPPSAMPLES; then - as_fn_error $? "not found" "$LINENO" 5 + as_fn_error $? "not found" "$LINENO" 5 elif test ! -d $IPPSAMPLES/speech-codecs; then - as_fn_error $? "directory doesn't seem to be valid" "$LINENO" 5 + as_fn_error $? "directory doesn't seem to be valid" "$LINENO" 5 else - # Remove trailing backslash - IPPSAMPLES=`echo $IPPSAMPLES | sed 's/\/$//'` + # Remove trailing backslash + IPPSAMPLES=`echo $IPPSAMPLES | sed 's/\/$//'` - # Guess the libusc.a/libspeech.a build location - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking Intel IPP USC build location" >&5 + # Guess the libusc.a/libspeech.a build location + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking Intel IPP USC build location" >&5 printf %s "checking Intel IPP USC build location... " >&6; } - if test -d $IPPSAMPLES/speech-codecs/bin; then - IPPVER=5 - IPPSAMP_DIR=`ls -d $IPPSAMPLES/speech-codecs/bin/*gcc*/lib | head -1` - elif test -d $IPPSAMPLES/speech-codecs/_bin; then - IPPVER=6 - if test -d $IPPSAMPLES/speech-codecs/_bin/*gcc*; then - # gcc compiler - IPPSAMP_DIR=`ls -d $IPPSAMPLES/speech-codecs/_bin/*gcc*/lib | head -1` - elif test -d $IPPSAMPLES/speech-codecs/_bin/*icc*; then - # icc compiler - IPPSAMP_DIR=`ls -d $IPPSAMPLES/speech-codecs/_bin/*icc*/lib | head -1` - else - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 + if test -d $IPPSAMPLES/speech-codecs/bin; then + IPPVER=5 + IPPSAMP_DIR=`ls -d $IPPSAMPLES/speech-codecs/bin/*gcc*/lib | head -1` + elif test -d $IPPSAMPLES/speech-codecs/_bin; then + IPPVER=6 + if test -d $IPPSAMPLES/speech-codecs/_bin/*gcc*; then + # gcc compiler + IPPSAMP_DIR=`ls -d $IPPSAMPLES/speech-codecs/_bin/*gcc*/lib | head -1` + elif test -d $IPPSAMPLES/speech-codecs/_bin/*icc*; then + # icc compiler + IPPSAMP_DIR=`ls -d $IPPSAMPLES/speech-codecs/_bin/*icc*/lib | head -1` + else + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "Unable to find to find built binaries under $IPPSAMPLES/speech-codecs/{bin,_bin}. Have you built the IPP samples? See \`config.log' for more details" "$LINENO" 5; } - fi - else - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 + fi + else + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "unable to find $IPPSAMPLES/speech-codecs/bin/*gcc*/lib or $IPPSAMPLES/speech-codecs/_bin/*gcc*/lib directory. Have you built the samples? See \`config.log' for more details" "$LINENO" 5; } - fi + fi - # Test the directory - if test ! -d $IPPSAMP_DIR; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 + # Test the directory + if test ! -d $IPPSAMP_DIR; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "There's something wrong with this script, directory $IPPSAMP_DIR does not exist See \`config.log' for more details" "$LINENO" 5; } - exit 1; - fi + exit 1; + fi - if test "x$IPPVER" = "x5"; then - IPPSAMP_LIBS="libusc.a" - IPPSAMP_LDLIBS="-lusc" - elif test "x$IPPVER" = "x6"; then - IPPSAMP_LIBS="libspeech.a" - IPPSAMP_LDLIBS="-lspeech" - else - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 + if test "x$IPPVER" = "x5"; then + IPPSAMP_LIBS="libusc.a" + IPPSAMP_LDLIBS="-lusc" + elif test "x$IPPVER" = "x6"; then + IPPSAMP_LIBS="libspeech.a" + IPPSAMP_LDLIBS="-lspeech" + else + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "bug in this script: unsupported IPP version See \`config.log' for more details" "$LINENO" 5; } - fi + fi - if test ! -f $IPPSAMP_DIR/$IPPSAMP_LIBS; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 + if test ! -f $IPPSAMP_DIR/$IPPSAMP_LIBS; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "$IPPSAMP_LIBS doesn't exist in $IPPSAMP_DIR See \`config.log' for more details" "$LINENO" 5; } - fi + fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $IPPSAMP_DIR" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $IPPSAMP_DIR" >&5 printf "%s\n" "$IPPSAMP_DIR" >&6; } - SAVED_CFLAGS="$CFLAGS" - SAVED_LDFLAGS="$LDFLAGS" - SAVED_LIBS="$LIBS" + SAVED_CFLAGS="$CFLAGS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_LIBS="$LIBS" - IPPSAMP_INC="-I$IPPSAMPLES/speech-codecs/core/usc/include" - CFLAGS="$CFLAGS $IPPSAMP_INC" - LDFLAGS="$LDFLAGS -L$IPPSAMP_DIR" - LIBS="$IPPSAMP_LDLIBS $LIBS" + IPPSAMP_INC="-I$IPPSAMPLES/speech-codecs/core/usc/include" + CFLAGS="$CFLAGS $IPPSAMP_INC" + LDFLAGS="$LDFLAGS -L$IPPSAMP_DIR" + LIBS="$IPPSAMP_LDLIBS $LIBS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking Intel IPP USC usability" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking Intel IPP USC usability" >&5 printf %s "checking Intel IPP USC usability... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #include int main (void) { @@ -9005,6 +9216,7 @@ extern USC_Fxns USC_G729AFP_Fxns; ; return 0; } + _ACEOF if ac_fn_c_try_link "$LINENO" then : @@ -9015,17 +9227,18 @@ else $as_nop printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no See \`config.log' for more details" "$LINENO" 5; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - CFLAGS="$SAVED_CFLAGS" - LDFLAGS="$IPP_LDFLAGS $SAVED_LDFLAGS" - LIBS="$IPP_LIBS $SAVED_LIBS" + CFLAGS="$SAVED_CFLAGS" + LDFLAGS="$IPP_LDFLAGS $SAVED_LDFLAGS" + LIBS="$IPP_LIBS $SAVED_LIBS" - IPP_CFLAGS="$IPP_CFLAGS $IPPSAMP_INC" - IPP_LDFLAGS="$IPP_LDFLAGS -L$IPPSAMP_DIR" - IPP_LIBS="$IPPSAMP_LDLIBS $IPP_LIBS" + IPP_CFLAGS="$IPP_CFLAGS $IPPSAMP_INC" + IPP_LDFLAGS="$IPP_LDFLAGS -L$IPPSAMP_DIR" + IPP_LIBS="$IPPSAMP_LDLIBS $IPP_LIBS" fi CFLAGS="$CFLAGS $IPP_CFLAGS" @@ -9033,9 +9246,9 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \ LIBS="$LIBS $IPP_LIBS" ac_build_mak_vars="$ac_build_mak_vars\n\ -export IPP_CFLAGS=$IPP_CFLAGS\n\ -export IPP_LDFLAGS=$IPP_LDFLAGS\n\ -export IPP_LIBS=$IPP_LIBS" + export IPP_CFLAGS=$IPP_CFLAGS\n\ + export IPP_LDFLAGS=$IPP_LDFLAGS\n\ + export IPP_LIBS=$IPP_LIBS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Skipping Intel IPP settings (not wanted)" >&5 printf "%s\n" "Skipping Intel IPP settings (not wanted)" >&6; } @@ -9046,16 +9259,18 @@ fi # Check whether --enable-android-mediacodec was given. if test ${enable_android_mediacodec+y} then : - enableval=$enable_android_mediacodec; if test "$enable_android_mediacodec" = "no"; then - ac_no_mediacodec=1 - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Android MediaCodec support is disabled... yes" >&5 + enableval=$enable_android_mediacodec; + if test "$enable_android_mediacodec" = "no"; then + ac_no_mediacodec=1 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Android MediaCodec support is disabled... yes" >&5 printf "%s\n" "Checking if Android MediaCodec support is disabled... yes" >&6; } - fi + fi + else $as_nop - case $target in - *android*) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for AMediaCodec_createDecoderByType in -lmediandk" >&5 + case $target in + *android*) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for AMediaCodec_createDecoderByType in -lmediandk" >&5 printf %s "checking for AMediaCodec_createDecoderByType in -lmediandk... " >&6; } if test ${ac_cv_lib_mediandk_AMediaCodec_createDecoderByType+y} then : @@ -9092,20 +9307,23 @@ fi printf "%s\n" "$ac_cv_lib_mediandk_AMediaCodec_createDecoderByType" >&6; } if test "x$ac_cv_lib_mediandk_AMediaCodec_createDecoderByType" = xyes then : - ac_pjmedia_has_amediacodec=1 && LIBS="-lmediandk $LIBS" + + ac_pjmedia_has_amediacodec=1 && LIBS="-lmediandk $LIBS" fi - if test "x$ac_pjmedia_has_amediacodec" = "x1"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Android AMediaCodec library is available... yes" >&5 + + if test "x$ac_pjmedia_has_amediacodec" = "x1"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Android AMediaCodec library is available... yes" >&5 printf "%s\n" "Checking if Android AMediaCodec library is available... yes" >&6; } - printf "%s\n" "#define PJMEDIA_HAS_ANDROID_MEDIACODEC 1" >>confdefs.h + printf "%s\n" "#define PJMEDIA_HAS_ANDROID_MEDIACODEC 1" >>confdefs.h - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Android AMediaCodec library is available... no" >&5 + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Android AMediaCodec library is available... no" >&5 printf "%s\n" "Checking if Android AMediaCodec library is available... no" >&6; } - fi - ;; - esac + fi + ;; + esac + fi @@ -9145,53 +9363,61 @@ ac_ssl_has_aes_gcm=0 # Check whether --enable-darwin-ssl was given. if test ${enable_darwin_ssl+y} then : - enableval=$enable_darwin_ssl; if test "$enable_darwin_ssl" = "no"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Darwin SSL support is disabled... yes" >&5 + enableval=$enable_darwin_ssl; + if test "$enable_darwin_ssl" = "no"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Darwin SSL support is disabled... yes" >&5 printf "%s\n" "Checking if Darwin SSL support is disabled... yes" >&6; } - fi + fi + else $as_nop - case $target in - *darwin*) - SAVED_CFLAGS="$CFLAGS" - CFLAGS="-Werror" - SAVED_LIBS="$LIBS" - LIBS="-framework Security" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + case $target in + *darwin*) + SAVED_CFLAGS="$CFLAGS" + CFLAGS="-Werror" + SAVED_LIBS="$LIBS" + + LIBS="-framework Security" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + + #include int main (void) { if (__builtin_available(macOS 10.12, iOS 10.0, *)) { - SSLContextRef ssl_ctx; - SSLReHandshake(ssl_ctx); - } + SSLContextRef ssl_ctx; + SSLReHandshake(ssl_ctx); + } ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_ssl_backend=darwin fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS="$SAVED_CFLAGS" - LIBS="$SAVED_LIBS" - if test "x$ac_ssl_backend" = "xdarwin"; then - printf "%s\n" "#define PJ_HAS_SSL_SOCK 1" >>confdefs.h - printf "%s\n" "#define PJ_SSL_SOCK_IMP PJ_SSL_SOCK_IMP_DARWIN" >>confdefs.h + CFLAGS="$SAVED_CFLAGS" + LIBS="$SAVED_LIBS" + if test "x$ac_ssl_backend" = "xdarwin"; then + printf "%s\n" "#define PJ_HAS_SSL_SOCK 1" >>confdefs.h + + printf "%s\n" "#define PJ_SSL_SOCK_IMP PJ_SSL_SOCK_IMP_DARWIN" >>confdefs.h - LIBS="$LIBS -framework Security" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Darwin SSL is available... yes" >&5 + LIBS="$LIBS -framework Security" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Darwin SSL is available... yes" >&5 printf "%s\n" "Checking if Darwin SSL is available... yes" >&6; } - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Darwin SSL is available... no" >&5 + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Darwin SSL is available... no" >&5 printf "%s\n" "Checking if Darwin SSL is available... no" >&6; } - fi - ;; - esac + fi + ;; + esac + fi @@ -9200,37 +9426,38 @@ fi if test ${enable_ssl+y} then : enableval=$enable_ssl; - if test "$enable_ssl" = "no"; then - ac_no_ssl=1 - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if SSL support is disabled... yes" >&5 + if test "$enable_ssl" = "no"; then + ac_no_ssl=1 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if SSL support is disabled... yes" >&5 printf "%s\n" "Checking if SSL support is disabled... yes" >&6; } - fi + fi else $as_nop - if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then - CFLAGS="$CFLAGS -I$with_ssl/include" - CPPFLAGS="$CPPFLAGS -I$with_ssl/include" - LDFLAGS="$LDFLAGS -L$with_ssl/lib" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using SSL prefix... $with_ssl" >&5 + if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then + CFLAGS="$CFLAGS -I$with_ssl/include" + CPPFLAGS="$CPPFLAGS -I$with_ssl/include" + LDFLAGS="$LDFLAGS -L$with_ssl/lib" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using SSL prefix... $with_ssl" >&5 printf "%s\n" "Using SSL prefix... $with_ssl" >&6; } - fi + fi - if test "x$with_gnutls" = "xno"; then - # We still need to check for OpenSSL installations even if - # we find Darwin SSL above since DTLS requires OpenSSL. - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: checking for OpenSSL installations.." >&5 + if test "x$with_gnutls" = "xno"; then + # We still need to check for OpenSSL installations even if + # we find Darwin SSL above since DTLS requires OpenSSL. + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: checking for OpenSSL installations.." >&5 printf "%s\n" "checking for OpenSSL installations.." >&6; } - ac_fn_c_check_header_compile "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default" + ac_fn_c_check_header_compile "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default" if test "x$ac_cv_header_openssl_ssl_h" = xyes then : openssl_h_present=1 fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ERR_load_BIO_strings in -lcrypto" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ERR_load_BIO_strings in -lcrypto" >&5 printf %s "checking for ERR_load_BIO_strings in -lcrypto... " >&6; } if test ${ac_cv_lib_crypto_ERR_load_BIO_strings+y} then : @@ -9267,9 +9494,11 @@ fi printf "%s\n" "$ac_cv_lib_crypto_ERR_load_BIO_strings" >&6; } if test "x$ac_cv_lib_crypto_ERR_load_BIO_strings" = xyes then : - libcrypto_present=1 && LIBS="-lcrypto $LIBS" + + libcrypto_present=1 && LIBS="-lcrypto $LIBS" else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ERR_get_error in -lcrypto" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ERR_get_error in -lcrypto" >&5 printf %s "checking for ERR_get_error in -lcrypto... " >&6; } if test ${ac_cv_lib_crypto_ERR_get_error+y} then : @@ -9309,9 +9538,12 @@ then : libcrypto_present=1 && LIBS="-lcrypto $LIBS" fi + + fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for SSL_CTX_new in -lssl" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for SSL_CTX_new in -lssl" >&5 printf %s "checking for SSL_CTX_new in -lssl... " >&6; } if test ${ac_cv_lib_ssl_SSL_CTX_new+y} then : @@ -9351,19 +9583,22 @@ then : libssl_present=1 && LIBS="-lssl $LIBS" fi - if test "x$openssl_h_present" = "x1" -a "x$libssl_present" = "x1" -a "x$libcrypto_present" = "x1"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: OpenSSL library found, SSL support enabled" >&5 + + if test "x$openssl_h_present" = "x1" -a "x$libssl_present" = "x1" -a "x$libcrypto_present" = "x1"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: OpenSSL library found, SSL support enabled" >&5 printf "%s\n" "OpenSSL library found, SSL support enabled" >&6; } - # Check if SRTP should be compiled with OpenSSL - # support, to enable cryptos such as AES GCM. + # Check if SRTP should be compiled with OpenSSL + # support, to enable cryptos such as AES GCM. - # EVP_CIPHER_CTX is now opaque in OpenSSL 1.1.0, libsrtp 1.5.4 uses it as a transparent type. - # Update 2.7: our bundled libsrtp has been upgraded to 2.1.0, - # so we can omit EVP_CIPHER_CTX definition check now. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + # EVP_CIPHER_CTX is now opaque in OpenSSL 1.1.0, libsrtp 1.5.4 uses it as a transparent type. + # Update 2.7: our bundled libsrtp has been upgraded to 2.1.0, + # so we can omit EVP_CIPHER_CTX definition check now. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + + #include int main (void) { @@ -9371,10 +9606,12 @@ EVP_CIPHER_CTX *ctx;EVP_aes_128_gcm(); ; return 0; } + _ACEOF if ac_fn_c_try_compile "$LINENO" then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for EVP_aes_128_gcm in -lcrypto" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for EVP_aes_128_gcm in -lcrypto" >&5 printf %s "checking for EVP_aes_128_gcm in -lcrypto... " >&6; } if test ${ac_cv_lib_crypto_EVP_aes_128_gcm+y} then : @@ -9414,42 +9651,43 @@ then : ac_ssl_has_aes_gcm=1 fi + + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - if test "x$ac_ssl_has_aes_gcm" = "x1"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: OpenSSL has AES GCM support, SRTP will use OpenSSL" >&5 + + if test "x$ac_ssl_has_aes_gcm" = "x1"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: OpenSSL has AES GCM support, SRTP will use OpenSSL" >&5 printf "%s\n" "OpenSSL has AES GCM support, SRTP will use OpenSSL" >&6; } - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: OpenSSL AES GCM support not found, SRTP will only support AES CM cryptos" >&5 + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: OpenSSL AES GCM support not found, SRTP will only support AES CM cryptos" >&5 printf "%s\n" "OpenSSL AES GCM support not found, SRTP will only support AES CM cryptos" >&6; } - fi + fi - if test "x$ac_ssl_backend" = "x"; then - # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK - #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1) - printf "%s\n" "#define PJ_HAS_SSL_SOCK 1" >>confdefs.h + if test "x$ac_ssl_backend" = "x"; then + # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK + #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1) + printf "%s\n" "#define PJ_HAS_SSL_SOCK 1" >>confdefs.h - printf "%s\n" "#define PJ_SSL_SOCK_IMP PJ_SSL_SOCK_IMP_OPENSSL" >>confdefs.h + printf "%s\n" "#define PJ_SSL_SOCK_IMP PJ_SSL_SOCK_IMP_OPENSSL" >>confdefs.h - ac_ssl_backend="openssl" - fi - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ** OpenSSL libraries not found **" >&5 + ac_ssl_backend="openssl" + fi + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ** OpenSSL libraries not found **" >&5 printf "%s\n" "** OpenSSL libraries not found **" >&6; } - fi - - fi - - if test "x$ac_ssl_backend" = "x"; then + fi + fi - if test "x$with_gnutls" != "xno" -a "x$with_gnutls" != "x"; then - CFLAGS="$CFLAGS -I$with_gnutls/include" - LDFLAGS="$LDFLAGS -L$with_gnutls/lib" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using GnuTLS prefix... $with_gnutls" >&5 + if test "x$ac_ssl_backend" = "x"; then + if test "x$with_gnutls" != "xno" -a "x$with_gnutls" != "x"; then + CFLAGS="$CFLAGS -I$with_gnutls/include" + LDFLAGS="$LDFLAGS -L$with_gnutls/lib" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using GnuTLS prefix... $with_gnutls" >&5 printf "%s\n" "Using GnuTLS prefix... $with_gnutls" >&6; } - fi + fi - for ac_prog in $host-pkg-config pkg-config "python pkgconfig.py" + for ac_prog in $host-pkg-config pkg-config "python pkgconfig.py" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 @@ -9497,23 +9735,24 @@ fi done test -n "$PKG_CONFIG" || PKG_CONFIG="none" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: checking for GnuTLS installations.." >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: checking for GnuTLS installations.." >&5 printf "%s\n" "checking for GnuTLS installations.." >&6; } - ac_fn_c_check_header_compile "$LINENO" "gnutls/gnutls.h" "ac_cv_header_gnutls_gnutls_h" "$ac_includes_default" + ac_fn_c_check_header_compile "$LINENO" "gnutls/gnutls.h" "ac_cv_header_gnutls_gnutls_h" "$ac_includes_default" if test "x$ac_cv_header_gnutls_gnutls_h" = xyes then : gnutls_h_present=1 fi - if test "$PKG_CONFIG" != "none"; then - if $PKG_CONFIG --exists gnutls; then - LIBS="$LIBS `$PKG_CONFIG --libs gnutls`" - libgnutls_present=1 - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gnutls_certificate_set_x509_system_trust in -lgnutls" >&5 + if test "$PKG_CONFIG" != "none"; then + if $PKG_CONFIG --exists gnutls; then + LIBS="$LIBS `$PKG_CONFIG --libs gnutls`" + libgnutls_present=1 + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gnutls_certificate_set_x509_system_trust in -lgnutls" >&5 printf %s "checking for gnutls_certificate_set_x509_system_trust in -lgnutls... " >&6; } if test ${ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust+y} then : @@ -9550,30 +9789,31 @@ fi printf "%s\n" "$ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust" >&6; } if test "x$ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust" = xyes then : - libgnutls_present=1 && - LIBS="$LIBS -lgnutls" + + libgnutls_present=1 && LIBS="$LIBS -lgnutls" fi - fi - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: *** Warning: neither pkg-config nor python is available, disabling gnutls. ***" >&5 + fi + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: *** Warning: neither pkg-config nor python is available, disabling gnutls. ***" >&5 printf "%s\n" "*** Warning: neither pkg-config nor python is available, disabling gnutls. ***" >&6; } - fi + fi - if test "x$gnutls_h_present" = "x1" -a "x$libgnutls_present" = "x1"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: GnuTLS library found, SSL support enabled" >&5 + if test "x$gnutls_h_present" = "x1" -a "x$libgnutls_present" = "x1"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: GnuTLS library found, SSL support enabled" >&5 printf "%s\n" "GnuTLS library found, SSL support enabled" >&6; } - printf "%s\n" "#define PJ_HAS_SSL_SOCK 1" >>confdefs.h + printf "%s\n" "#define PJ_HAS_SSL_SOCK 1" >>confdefs.h - printf "%s\n" "#define PJ_SSL_SOCK_IMP PJ_SSL_SOCK_IMP_GNUTLS" >>confdefs.h + printf "%s\n" "#define PJ_SSL_SOCK_IMP PJ_SSL_SOCK_IMP_GNUTLS" >>confdefs.h - ac_ssl_backend="gnutls" - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ** No GnuTLS libraries found, disabling SSL support **" >&5 + ac_ssl_backend="gnutls" + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ** No GnuTLS libraries found, disabling SSL support **" >&5 printf "%s\n" "** No GnuTLS libraries found, disabling SSL support **" >&6; } - fi + fi + + fi - fi fi @@ -9626,44 +9866,46 @@ fi if test ${enable_opencore_amr+y} then : enableval=$enable_opencore_amr; - if test "$enable_opencore_amr" = "no"; then - ac_no_opencore_amrnb=1 - ac_no_opencore_amrwb=1 - printf "%s\n" "#define PJMEDIA_HAS_OPENCORE_AMRNB_CODEC 0" >>confdefs.h + if test "$enable_opencore_amr" = "no"; then + ac_no_opencore_amrnb=1 + ac_no_opencore_amrwb=1 + printf "%s\n" "#define PJMEDIA_HAS_OPENCORE_AMRNB_CODEC 0" >>confdefs.h - printf "%s\n" "#define PJMEDIA_HAS_OPENCORE_AMRWB_CODEC 0" >>confdefs.h + printf "%s\n" "#define PJMEDIA_HAS_OPENCORE_AMRWB_CODEC 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if OpenCORE AMR support is disabled... yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if OpenCORE AMR support is disabled... yes" >&5 printf "%s\n" "Checking if OpenCORE AMR support is disabled... yes" >&6; } - fi + fi else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: checking for OpenCORE AMR installations.." >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: checking for OpenCORE AMR installations.." >&5 printf "%s\n" "checking for OpenCORE AMR installations.." >&6; } - if test "x$with_opencore_amr" != "xno" -a "x$with_opencore_amr" != "x"; then - CFLAGS="$CFLAGS -I$with_opencore_amr/include" - CPPFLAGS="$CPPFLAGS -I$with_opencore_amr/include" - LDFLAGS="$LDFLAGS -L$with_opencore_amr/lib" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using OpenCORE AMR prefix... $with_opencore_amr" >&5 + if test "x$with_opencore_amr" != "xno" -a "x$with_opencore_amr" != "x"; then + CFLAGS="$CFLAGS -I$with_opencore_amr/include" + CPPFLAGS="$CPPFLAGS -I$with_opencore_amr/include" + LDFLAGS="$LDFLAGS -L$with_opencore_amr/lib" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using OpenCORE AMR prefix... $with_opencore_amr" >&5 printf "%s\n" "Using OpenCORE AMR prefix... $with_opencore_amr" >&6; } - fi - if test "x$with_opencore_amrwbenc" != "xno" -a "x$with_opencore_amrwbenc" != "x"; then - CFLAGS="$CFLAGS -I$with_opencore_amrwbenc/include" - CPPFLAGS="$CPPFLAGS -I$with_opencore_amrwbenc/include" - LDFLAGS="$LDFLAGS -L$with_opencore_amrwbenc/lib" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using OpenCORE AMRWB-enc prefix... $with_opencore_amrwbenc" >&5 + fi + + if test "x$with_opencore_amrwbenc" != "xno" -a "x$with_opencore_amrwbenc" != "x"; then + CFLAGS="$CFLAGS -I$with_opencore_amrwbenc/include" + CPPFLAGS="$CPPFLAGS -I$with_opencore_amrwbenc/include" + LDFLAGS="$LDFLAGS -L$with_opencore_amrwbenc/lib" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using OpenCORE AMRWB-enc prefix... $with_opencore_amrwbenc" >&5 printf "%s\n" "Using OpenCORE AMRWB-enc prefix... $with_opencore_amrwbenc" >&6; } - fi + fi + - ac_fn_c_check_header_compile "$LINENO" "opencore-amrnb/interf_enc.h" "ac_cv_header_opencore_amrnb_interf_enc_h" "$ac_includes_default" + ac_fn_c_check_header_compile "$LINENO" "opencore-amrnb/interf_enc.h" "ac_cv_header_opencore_amrnb_interf_enc_h" "$ac_includes_default" if test "x$ac_cv_header_opencore_amrnb_interf_enc_h" = xyes then : opencore_amrnb_h_present=1 fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Encoder_Interface_init in -lopencore-amrnb" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Encoder_Interface_init in -lopencore-amrnb" >&5 printf %s "checking for Encoder_Interface_init in -lopencore-amrnb... " >&6; } if test ${ac_cv_lib_opencore_amrnb_Encoder_Interface_init+y} then : @@ -9700,36 +9942,40 @@ fi printf "%s\n" "$ac_cv_lib_opencore_amrnb_Encoder_Interface_init" >&6; } if test "x$ac_cv_lib_opencore_amrnb_Encoder_Interface_init" = xyes then : - opencore_amrnb_present=1 && LIBS="$LIBS -lopencore-amrnb" + + opencore_amrnb_present=1 && LIBS="$LIBS -lopencore-amrnb" fi - if test "x$opencore_amrnb_h_present" = "x1" -a "x$opencore_amrnb_present" = "x1"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: OpenCORE AMR-NB library found, AMR-NB support enabled" >&5 + + if test "x$opencore_amrnb_h_present" = "x1" -a "x$opencore_amrnb_present" = "x1"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: OpenCORE AMR-NB library found, AMR-NB support enabled" >&5 printf "%s\n" "OpenCORE AMR-NB library found, AMR-NB support enabled" >&6; } - printf "%s\n" "#define PJMEDIA_HAS_OPENCORE_AMRNB_CODEC 1" >>confdefs.h + printf "%s\n" "#define PJMEDIA_HAS_OPENCORE_AMRNB_CODEC 1" >>confdefs.h + + else + ac_no_opencore_amrnb=1 + printf "%s\n" "#define PJMEDIA_HAS_OPENCORE_AMRNB_CODEC 0" >>confdefs.h - else - ac_no_opencore_amrnb=1 - printf "%s\n" "#define PJMEDIA_HAS_OPENCORE_AMRNB_CODEC 0" >>confdefs.h + fi - fi - ac_fn_c_check_header_compile "$LINENO" "vo-amrwbenc/enc_if.h" "ac_cv_header_vo_amrwbenc_enc_if_h" "$ac_includes_default" + ac_fn_c_check_header_compile "$LINENO" "vo-amrwbenc/enc_if.h" "ac_cv_header_vo_amrwbenc_enc_if_h" "$ac_includes_default" if test "x$ac_cv_header_vo_amrwbenc_enc_if_h" = xyes then : opencore_amrwb_enc_h_present=1 fi - ac_fn_c_check_header_compile "$LINENO" "opencore-amrwb/dec_if.h" "ac_cv_header_opencore_amrwb_dec_if_h" "$ac_includes_default" + ac_fn_c_check_header_compile "$LINENO" "opencore-amrwb/dec_if.h" "ac_cv_header_opencore_amrwb_dec_if_h" "$ac_includes_default" if test "x$ac_cv_header_opencore_amrwb_dec_if_h" = xyes then : opencore_amrwb_dec_h_present=1 fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for D_IF_init in -lopencore-amrwb" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for D_IF_init in -lopencore-amrwb" >&5 printf %s "checking for D_IF_init in -lopencore-amrwb... " >&6; } if test ${ac_cv_lib_opencore_amrwb_D_IF_init+y} then : @@ -9766,10 +10012,12 @@ fi printf "%s\n" "$ac_cv_lib_opencore_amrwb_D_IF_init" >&6; } if test "x$ac_cv_lib_opencore_amrwb_D_IF_init" = xyes then : - opencore_amrwb_dec_present=1 && LIBS="$LIBS -lopencore-amrwb" + + opencore_amrwb_dec_present=1 && LIBS="$LIBS -lopencore-amrwb" fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for E_IF_init in -lvo-amrwbenc" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for E_IF_init in -lvo-amrwbenc" >&5 printf %s "checking for E_IF_init in -lvo-amrwbenc... " >&6; } if test ${ac_cv_lib_vo_amrwbenc_E_IF_init+y} then : @@ -9806,19 +10054,21 @@ fi printf "%s\n" "$ac_cv_lib_vo_amrwbenc_E_IF_init" >&6; } if test "x$ac_cv_lib_vo_amrwbenc_E_IF_init" = xyes then : - opencore_amrwb_enc_present=1 && LIBS="$LIBS -lvo-amrwbenc" + + opencore_amrwb_enc_present=1 && LIBS="$LIBS -lvo-amrwbenc" fi - if test "x$opencore_amrwb_enc_h_present" = "x1" -a "x$opencore_amrwb_dec_h_present" = "x1" -a "x$opencore_amrwb_enc_present" = "x1" -a "x$opencore_amrwb_dec_present" = "x1"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: OpenCORE AMR-WB library found, AMR-WB support enabled" >&5 + + if test "x$opencore_amrwb_enc_h_present" = "x1" -a "x$opencore_amrwb_dec_h_present" = "x1" -a "x$opencore_amrwb_enc_present" = "x1" -a "x$opencore_amrwb_dec_present" = "x1"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: OpenCORE AMR-WB library found, AMR-WB support enabled" >&5 printf "%s\n" "OpenCORE AMR-WB library found, AMR-WB support enabled" >&6; } - printf "%s\n" "#define PJMEDIA_HAS_OPENCORE_AMRWB_CODEC 1" >>confdefs.h + printf "%s\n" "#define PJMEDIA_HAS_OPENCORE_AMRWB_CODEC 1" >>confdefs.h - else - ac_no_opencore_amrwb=1 - printf "%s\n" "#define PJMEDIA_HAS_OPENCORE_AMRWB_CODEC 0" >>confdefs.h + else + ac_no_opencore_amrwb=1 + printf "%s\n" "#define PJMEDIA_HAS_OPENCORE_AMRWB_CODEC 0" >>confdefs.h - fi + fi fi @@ -9844,34 +10094,35 @@ fi if test ${enable_silk+y} then : enableval=$enable_silk; - if test "$enable_silk" = "no"; then - ac_no_silk=1 - printf "%s\n" "#define PJMEDIA_HAS_SILK_CODEC 0" >>confdefs.h + if test "$enable_silk" = "no"; then + ac_no_silk=1 + printf "%s\n" "#define PJMEDIA_HAS_SILK_CODEC 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if SILK support is disabled... yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if SILK support is disabled... yes" >&5 printf "%s\n" "Checking if SILK support is disabled... yes" >&6; } - fi + fi else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: checking for SILK installations.." >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: checking for SILK installations.." >&5 printf "%s\n" "checking for SILK installations.." >&6; } - if test "x$with_silk" != "xno" -a "x$with_silk" != "x"; then - CFLAGS="$CFLAGS -I$with_silk/interface" - CPPFLAGS="$CPPFLAGS -I$with_silk/interface" - LDFLAGS="$LDFLAGS -L$with_silk" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using SILK prefix... $with_silk" >&5 + if test "x$with_silk" != "xno" -a "x$with_silk" != "x"; then + CFLAGS="$CFLAGS -I$with_silk/interface" + CPPFLAGS="$CPPFLAGS -I$with_silk/interface" + LDFLAGS="$LDFLAGS -L$with_silk" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using SILK prefix... $with_silk" >&5 printf "%s\n" "Using SILK prefix... $with_silk" >&6; } - fi + fi + - ac_fn_c_check_header_compile "$LINENO" "SKP_Silk_SDK_API.h" "ac_cv_header_SKP_Silk_SDK_API_h" "$ac_includes_default" + ac_fn_c_check_header_compile "$LINENO" "SKP_Silk_SDK_API.h" "ac_cv_header_SKP_Silk_SDK_API_h" "$ac_includes_default" if test "x$ac_cv_header_SKP_Silk_SDK_API_h" = xyes then : silk_h_present=1 fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for SKP_Silk_SDK_get_version in -lSKP_SILK_SDK" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for SKP_Silk_SDK_get_version in -lSKP_SILK_SDK" >&5 printf %s "checking for SKP_Silk_SDK_get_version in -lSKP_SILK_SDK... " >&6; } if test ${ac_cv_lib_SKP_SILK_SDK_SKP_Silk_SDK_get_version+y} then : @@ -9908,19 +10159,22 @@ fi printf "%s\n" "$ac_cv_lib_SKP_SILK_SDK_SKP_Silk_SDK_get_version" >&6; } if test "x$ac_cv_lib_SKP_SILK_SDK_SKP_Silk_SDK_get_version" = xyes then : - silk_present=1 && LIBS="$LIBS -lSKP_SILK_SDK" + + silk_present=1 && LIBS="$LIBS -lSKP_SILK_SDK" fi - if test "x$silk_h_present" = "x1" -a "x$silk_present" = "x1"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: SILK library found, SILK support enabled" >&5 + + if test "x$silk_h_present" = "x1" -a "x$silk_present" = "x1"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: SILK library found, SILK support enabled" >&5 printf "%s\n" "SILK library found, SILK support enabled" >&6; } - printf "%s\n" "#define PJMEDIA_HAS_SILK_CODEC 1" >>confdefs.h + printf "%s\n" "#define PJMEDIA_HAS_SILK_CODEC 1" >>confdefs.h - else - ac_no_silk=1 - printf "%s\n" "#define PJMEDIA_HAS_SILK_CODEC 0" >>confdefs.h + else + ac_no_silk=1 + printf "%s\n" "#define PJMEDIA_HAS_SILK_CODEC 0" >>confdefs.h + + fi - fi fi @@ -9945,34 +10199,34 @@ fi if test ${enable_opus+y} then : enableval=$enable_opus; - if test "$enable_opus" = "no"; then - ac_no_opus=1 - printf "%s\n" "#define PJMEDIA_HAS_OPUS_CODEC 0" >>confdefs.h + if test "$enable_opus" = "no"; then + ac_no_opus=1 + printf "%s\n" "#define PJMEDIA_HAS_OPUS_CODEC 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if OPUS support is disabled... yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if OPUS support is disabled... yes" >&5 printf "%s\n" "Checking if OPUS support is disabled... yes" >&6; } - fi + fi else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: checking for OPUS installations.." >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: checking for OPUS installations.." >&5 printf "%s\n" "checking for OPUS installations.." >&6; } - if test "x$with_opus" != "xno" -a "x$with_opus" != "x"; then - CFLAGS="$CFLAGS -I$with_opus/include" - CPPFLAGS="$CPPFLAGS -I$with_opus/include" - LDFLAGS="$LDFLAGS -L$with_opus/lib" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using OPUS prefix... $with_opus" >&5 + if test "x$with_opus" != "xno" -a "x$with_opus" != "x"; then + CFLAGS="$CFLAGS -I$with_opus/include" + CPPFLAGS="$CPPFLAGS -I$with_opus/include" + LDFLAGS="$LDFLAGS -L$with_opus/lib" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using OPUS prefix... $with_opus" >&5 printf "%s\n" "Using OPUS prefix... $with_opus" >&6; } - fi + fi - ac_fn_c_check_header_compile "$LINENO" "opus/opus.h" "ac_cv_header_opus_opus_h" "$ac_includes_default" + ac_fn_c_check_header_compile "$LINENO" "opus/opus.h" "ac_cv_header_opus_opus_h" "$ac_includes_default" if test "x$ac_cv_header_opus_opus_h" = xyes then : opus_h_present=1 fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for opus_repacketizer_get_size in -lopus" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for opus_repacketizer_get_size in -lopus" >&5 printf %s "checking for opus_repacketizer_get_size in -lopus... " >&6; } if test ${ac_cv_lib_opus_opus_repacketizer_get_size+y} then : @@ -10009,21 +10263,24 @@ fi printf "%s\n" "$ac_cv_lib_opus_opus_repacketizer_get_size" >&6; } if test "x$ac_cv_lib_opus_opus_repacketizer_get_size" = xyes then : - opus_present=1 && LIBS="-lopus $LIBS" + + opus_present=1 && LIBS="-lopus $LIBS" fi - if test "x$opus_h_present" = "x1" -a "x$opus_present" = "x1"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: OPUS library found, OPUS support enabled" >&5 + + if test "x$opus_h_present" = "x1" -a "x$opus_present" = "x1"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: OPUS library found, OPUS support enabled" >&5 printf "%s\n" "OPUS library found, OPUS support enabled" >&6; } - printf "%s\n" "#define PJMEDIA_HAS_OPUS_CODEC 1" >>confdefs.h + printf "%s\n" "#define PJMEDIA_HAS_OPUS_CODEC 1" >>confdefs.h - else - ac_no_opus=1 - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: OPUS library not found, OPUS support disabled" >&5 + else + ac_no_opus=1 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: OPUS library not found, OPUS support disabled" >&5 printf "%s\n" "OPUS library not found, OPUS support disabled" >&6; } - printf "%s\n" "#define PJMEDIA_HAS_OPUS_CODEC 0" >>confdefs.h + printf "%s\n" "#define PJMEDIA_HAS_OPUS_CODEC 0" >>confdefs.h + + fi - fi fi @@ -10048,73 +10305,75 @@ fi if test ${enable_bcg729+y} then : enableval=$enable_bcg729; - if test "$enable_bcg729" = "no"; then - ac_no_bcg729=1 - printf "%s\n" "#define PJMEDIA_HAS_BCG729 0" >>confdefs.h + if test "$enable_bcg729" = "no"; then + ac_no_bcg729=1 + printf "%s\n" "#define PJMEDIA_HAS_BCG729 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if bcg729 is disabled... yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if bcg729 is disabled... yes" >&5 printf "%s\n" "Checking if bcg729 is disabled... yes" >&6; } - fi + fi else $as_nop - if test "x$with_bcg729" != "xno" -a "x$with_bcg729" != "x"; then - BCG729_PREFIX=$with_bcg729 - BCG729_CFLAGS="-I$BCG729_PREFIX/include" - BCG729_LDFLAGS="-L$BCG729_PREFIX/lib" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using bcg729 prefix... $with_bcg729" >&5 + if test "x$with_bcg729" != "xno" -a "x$with_bcg729" != "x"; then + BCG729_PREFIX=$with_bcg729 + BCG729_CFLAGS="-I$BCG729_PREFIX/include" + BCG729_LDFLAGS="-L$BCG729_PREFIX/lib" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using bcg729 prefix... $with_bcg729" >&5 printf "%s\n" "Using bcg729 prefix... $with_bcg729" >&6; } - else - BCG729_CFLAGS="" - BCG729_LDFLAGS="" - fi + else + BCG729_CFLAGS="" + BCG729_LDFLAGS="" + fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking bcg729 usability" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking bcg729 usability" >&5 printf %s "checking bcg729 usability... " >&6; } - BCG729_LIBS="-lbcg729" + BCG729_LIBS="-lbcg729" - SAVED_LIBS="$LIBS" - SAVED_LDFLAGS="$LDFLAGS" - SAVED_CFLAGS="$CFLAGS" + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CFLAGS="$CFLAGS" - LIBS="$BCG729_LIBS $LIBS" - LDFLAGS="$BCG729_LDFLAGS $LDFLAGS" - CFLAGS="$BCG729_CFLAGS $CFLAGS" + LIBS="$BCG729_LIBS $LIBS" + LDFLAGS="$BCG729_LDFLAGS $LDFLAGS" + CFLAGS="$BCG729_CFLAGS $CFLAGS" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include - #include + + #include + #include int main (void) { initBcg729EncoderChannel(0); - ; return 0; } + _ACEOF if ac_fn_c_try_link "$LINENO" then : - printf "%s\n" "#define PJMEDIA_HAS_BCG729 1" >>confdefs.h + printf "%s\n" "#define PJMEDIA_HAS_BCG729 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop - ac_no_bcg729=1 - printf "%s\n" "#define PJMEDIA_HAS_BCG729 0" >>confdefs.h + ac_no_bcg729=1 + printf "%s\n" "#define PJMEDIA_HAS_BCG729 0" >>confdefs.h - LIBS="$SAVED_LIBS" - LDFLAGS="$SAVED_LDFLAGS" - CFLAGS="$SAVED_CFLAGS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CFLAGS="$SAVED_CFLAGS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -10144,131 +10403,136 @@ fi if test ${enable_lyra+y} then : enableval=$enable_lyra; - if test "$enable_lyra" = "no"; then - ac_no_lyra_codec=1 - printf "%s\n" "#define PJMEDIA_HAS_LYRA_CODEC 0" >>confdefs.h + if test "$enable_lyra" = "no"; then + ac_no_lyra_codec=1 + printf "%s\n" "#define PJMEDIA_HAS_LYRA_CODEC 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if lyra is disabled... yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if lyra is disabled... yes" >&5 printf "%s\n" "Checking if lyra is disabled... yes" >&6; } - fi + fi else $as_nop - if test "x$with_lyra" != "xno" -a "x$with_lyra" != "x"; then - LYRA_PREFIX=$with_lyra - LYRA_CPPFLAGS="-DGLOG_DEPRECATED=__attribute__((deprecated)) -DGLOG_EXPORT=__attribute__((visibility(\"default\"))) -DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))" - LYRA_CXXFLAGS="-std=c++17 -Wno-deprecated-builtins -I$LYRA_PREFIX/include/com_google_absl -I$LYRA_PREFIX/include/gulrak_filesystem -I$LYRA_PREFIX/include/com_google_glog/src -I$LYRA_PREFIX" - LYRA_LDFLAGS="-L$LYRA_PREFIX/lib" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using lyra prefix... $with_lyra" >&5 + if test "x$with_lyra" != "xno" -a "x$with_lyra" != "x"; then + LYRA_PREFIX=$with_lyra + LYRA_CPPFLAGS="-DGLOG_DEPRECATED=__attribute__((deprecated)) -DGLOG_EXPORT=__attribute__((visibility(\"default\"))) -DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))" + LYRA_CXXFLAGS="-std=c++17 -Wno-deprecated-builtins -I$LYRA_PREFIX/include/com_google_absl -I$LYRA_PREFIX/include/gulrak_filesystem -I$LYRA_PREFIX/include/com_google_glog/src -I$LYRA_PREFIX" + LYRA_LDFLAGS="-L$LYRA_PREFIX/lib" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using lyra prefix... $with_lyra" >&5 printf "%s\n" "Using lyra prefix... $with_lyra" >&6; } - else - LYRA_CXXFLAGS="" - LYRA_LDFLAGS="" - fi + else + LYRA_CXXFLAGS="" + LYRA_LDFLAGS="" + fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking lyra usability" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking lyra usability" >&5 printf %s "checking lyra usability... " >&6; } - LYRA_LIBS="-llyra" + LYRA_LIBS="-llyra" - SAVED_LIBS="$LIBS" - SAVED_LDFLAGS="$LDFLAGS" - SAVED_CXXFLAGS="$CXXFLAGS" - SAVED_CPPFLAGS="$CPPFLAGS" + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CXXFLAGS="$CXXFLAGS" + SAVED_CPPFLAGS="$CPPFLAGS" - LIBS="$LYRA_LIBS $LIBS" - LDFLAGS="$LYRA_LDFLAGS $LDFLAGS" - CXXFLAGS="$LYRA_CXXFLAGS $CXXFLAGS" - CPPFLAGS="$LYRA_CPPFLAGS $CPPFLAGS" + LIBS="$LYRA_LIBS $LIBS" + LDFLAGS="$LYRA_LDFLAGS $LDFLAGS" + CXXFLAGS="$LYRA_CXXFLAGS $CXXFLAGS" + CPPFLAGS="$LYRA_CPPFLAGS $CPPFLAGS" - ac_ext=cpp + ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include "lyra_decoder.h" + + #include "lyra_decoder.h" int main (void) { std::unique_ptr dec = chromemedia::codec::LyraDecoder::Create(8000,1,""); - ; return 0; } + _ACEOF if ac_fn_cxx_try_link "$LINENO" then : - ac_lyra_model_path="$LYRA_PREFIX/model_coeffs" - printf "%s\n" "#define PJMEDIA_HAS_LYRA_CODEC 1" >>confdefs.h + ac_lyra_model_path="$LYRA_PREFIX/model_coeffs" + printf "%s\n" "#define PJMEDIA_HAS_LYRA_CODEC 1" >>confdefs.h - LYRA_CPPFLAGS="'-DGLOG_DEPRECATED=__attribute__((deprecated))' '-DGLOG_EXPORT=__attribute__((visibility(\"default\")))' '-DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))'" - CXXFLAGS="$LYRA_CPPFLAGS $CXXFLAGS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + LYRA_CPPFLAGS="'-DGLOG_DEPRECATED=__attribute__((deprecated))' '-DGLOG_EXPORT=__attribute__((visibility(\"default\")))' '-DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))'" + CXXFLAGS="$LYRA_CPPFLAGS $CXXFLAGS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop - ac_no_lyra_codec=1 - LIBS="$SAVED_LIBS" - LDFLAGS="$SAVED_LDFLAGS" - CXXFLAGS="$SAVED_CXXFLAGS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 + ac_no_lyra_codec=1 + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CXXFLAGS="$SAVED_CXXFLAGS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } + fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - ac_ext=c + ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu - CPPFLAGS="$SAVED_CPPFLAGS" - -fi + CPPFLAGS="$SAVED_CPPFLAGS" +fi # Check whether --enable-libsrtp was given. if test ${enable_libsrtp+y} then : - enableval=$enable_libsrtp; if test "$enable_libsrtp" = "no"; then - ac_no_srtp=1 - CFLAGS="$CFLAGS -DPJMEDIA_HAS_SRTP=0" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libsrtp is disabled...yes" >&5 + enableval=$enable_libsrtp; + if test "$enable_libsrtp" = "no"; then + ac_no_srtp=1 + CFLAGS="$CFLAGS -DPJMEDIA_HAS_SRTP=0" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libsrtp is disabled...yes" >&5 printf "%s\n" "Checking if libsrtp is disabled...yes" >&6; } - fi + fi + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libsrtp is disabled...no" >&5 printf "%s\n" "Checking if libsrtp is disabled...no" >&6; } -fi +fi # Check whether --enable-libyuv was given. if test ${enable_libyuv+y} then : - enableval=$enable_libyuv; if test "$enable_libyuv" = "no"; then - ac_no_yuv=1 - printf "%s\n" "#define PJMEDIA_HAS_LIBYUV 0" >>confdefs.h + enableval=$enable_libyuv; + if test "$enable_libyuv" = "no"; then + ac_no_yuv=1 + printf "%s\n" "#define PJMEDIA_HAS_LIBYUV 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libyuv is disabled...yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libyuv is disabled...yes" >&5 printf "%s\n" "Checking if libyuv is disabled...yes" >&6; } - fi + fi + else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libyuv is disabled...no" >&5 printf "%s\n" "Checking if libyuv is disabled...no" >&6; } -fi +fi @@ -10278,82 +10542,85 @@ fi # Check whether --enable-libwebrtc was given. if test ${enable_libwebrtc+y} then : - enableval=$enable_libwebrtc; if test "$enable_libwebrtc" = "no"; then - ac_no_webrtc=1 - printf "%s\n" "#define PJMEDIA_HAS_LIBWEBRTC 0" >>confdefs.h + enableval=$enable_libwebrtc; + if test "$enable_libwebrtc" = "no"; then + ac_no_webrtc=1 + printf "%s\n" "#define PJMEDIA_HAS_LIBWEBRTC 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libwebrtc is disabled...yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libwebrtc is disabled...yes" >&5 printf "%s\n" "Checking if libwebrtc is disabled...yes" >&6; } - fi + fi + else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libwebrtc is disabled...no" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libwebrtc is disabled...no" >&5 printf "%s\n" "Checking if libwebrtc is disabled...no" >&6; } - case $target in - *-apple-darwin_ios*) - case $target in - arm64*) - ac_webrtc_instset=neon - ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" - ;; - *arm*) - ac_webrtc_instset=neon - ;; - *) - ac_webrtc_instset=sse2 - ;; - esac - ;; - *android*) - case $TARGET_ABI in - armeabi-v7a) - ac_webrtc_instset=neon - ac_webrtc_cflags="-mfloat-abi=softfp -mfpu=neon" - ;; - armeabi) - ac_webrtc_instset=neon - ac_webrtc_cflags="-mthumb -mfloat-abi=softfp -mfpu=neon -march=armv7" - ;; - arm64*) - ac_webrtc_instset=neon - ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" - ;; - mips64*) - ac_webrtc_instset=generic - ;; - mips*) - ac_webrtc_instset=mips - ;; - x86*) - ac_webrtc_instset=sse2 - ;; - *) - ac_webrtc_instset=generic - ;; - esac - ;; - *mingw* | *cygw*) - ac_webrtc_instset=sse2 - ac_webrtc_cflags="-msse2" - ;; - *win32* | *w32* | *darwin* | *linux*) - case $target in - armv7l*gnueabihf) - ac_webrtc_instset=neon - ac_webrtc_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" - ;; - arm-apple-darwin* | aarch64*) - ac_webrtc_instset=neon - ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" - ;; - *) - ac_webrtc_instset=sse2 - ;; - esac - ;; - *) - ;; - esac + case $target in + *-apple-darwin_ios*) + case $target in + arm64*) + ac_webrtc_instset=neon + ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" + ;; + *arm*) + ac_webrtc_instset=neon + ;; + *) + ac_webrtc_instset=sse2 + ;; + esac + ;; + *android*) + case $TARGET_ABI in + armeabi-v7a) + ac_webrtc_instset=neon + ac_webrtc_cflags="-mfloat-abi=softfp -mfpu=neon" + ;; + armeabi) + ac_webrtc_instset=neon + ac_webrtc_cflags="-mthumb -mfloat-abi=softfp -mfpu=neon -march=armv7" + ;; + arm64*) + ac_webrtc_instset=neon + ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" + ;; + mips64*) + ac_webrtc_instset=generic + ;; + mips*) + ac_webrtc_instset=mips + ;; + x86*) + ac_webrtc_instset=sse2 + ;; + *) + ac_webrtc_instset=generic + ;; + esac + ;; + *mingw* | *cygw*) + ac_webrtc_instset=sse2 + ac_webrtc_cflags="-msse2" + ;; + *win32* | *w32* | *darwin* | *linux*) + case $target in + armv7l*gnueabihf) + ac_webrtc_instset=neon + ac_webrtc_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" + ;; + arm-apple-darwin* | aarch64*) + ac_webrtc_instset=neon + ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" + ;; + *) + ac_webrtc_instset=sse2 + ;; + esac + ;; + *) + ;; + esac + fi @@ -10367,23 +10634,24 @@ fi if test ${enable_libwebrtc_aec3+y} then : enableval=$enable_libwebrtc_aec3; - if test "$enable_libwebrtc_aec3" = "yes"; then - # Test if we can build WebRtc AEC3 - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if WebRtc AEC3 can be compiled with C++17" >&5 + if test "$enable_libwebrtc_aec3" = "yes"; then + # Test if we can build WebRtc AEC3 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if WebRtc AEC3 can be compiled with C++17" >&5 printf %s "checking if WebRtc AEC3 can be compiled with C++17... " >&6; } - SAVED_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS -std=c++17" + SAVED_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -std=c++17" - ac_ext=cpp + ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ + int main (void) { @@ -10391,113 +10659,120 @@ main (void) ; return 0; } + _ACEOF if ac_fn_cxx_try_link "$LINENO" then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - LD="$CXX" + LD="$CXX" + else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - CXXFLAGS="$SAVED_CXXFLAGS" - ac_no_webrtc_aec3=1 - printf "%s\n" "#define PJMEDIA_HAS_LIBWEBRTC_AEC3 0" >>confdefs.h + CXXFLAGS="$SAVED_CXXFLAGS" + ac_no_webrtc_aec3=1 + printf "%s\n" "#define PJMEDIA_HAS_LIBWEBRTC_AEC3 0" >>confdefs.h + + fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - ac_ext=c + ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu - case $target in - *-apple-darwin_ios*) - case $target in - arm64*) - ac_webrtc_aec3_instset=neon - ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" - ;; - *arm*) - ac_webrtc_aec3_instset=neon - ;; - *) - ac_webrtc_aec3_instset=sse2 - ;; - esac - ac_webrtc_aec3_cflags+=" -DWEBRTC_IOS=1" - ;; - *android*) - case $TARGET_ABI in - armeabi-v7a) - ac_webrtc_aec3_instset=neon - ac_webrtc_aec3_cflags="-mfloat-abi=softfp -mfpu=neon" - ;; - armeabi) - ac_webrtc_aec3_instset=neon - ac_webrtc_aec3_cflags="-mthumb -mfloat-abi=softfp -mfpu=neon -march=armv7" - ;; - arm64*) - ac_webrtc_aec3_instset=neon - ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" - ;; - x86*) - ac_webrtc_aec3_instset=sse2 - ;; - esac - ac_webrtc_aec3_cflags+=" -DWEBRTC_ANDROID=1" - ;; - *mingw* | *cygw*) - ac_webrtc_aec3_instset=sse2 - ac_webrtc_aec3_cflags="-msse2" - ;; - *win32* | *w32* | *darwin* | *linux*) - case $target in - armv7l*gnueabihf) - ac_webrtc_aec3_instset=neon - ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" - ;; - arm-apple-darwin* | aarch64*) - ac_webrtc_aec3_instset=neon - ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" - ;; - *) - ac_webrtc_aec3_instset=sse2 - ;; - esac - case $target in - *darwin*) - ac_webrtc_aec3_cflags+=" -DWEBRTC_MAC=1" - ;; - *linux*) - ac_webrtc_aec3_cflags+=" -DWEBRTC_LINUX=1" - ;; - esac - ;; - *) - ;; - esac - ac_webrtc_aec3_cflags+=" -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_POSIX=1" - else - ac_no_webrtc_aec3=1 - printf "%s\n" "#define PJMEDIA_HAS_LIBWEBRTC_AEC3 0" >>confdefs.h - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libwebrtc-aec3 is enabled...no" >&5 + case $target in + *-apple-darwin_ios*) + case $target in + arm64*) + ac_webrtc_aec3_instset=neon + ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" + ;; + *arm*) + ac_webrtc_aec3_instset=neon + ;; + *) + ac_webrtc_aec3_instset=sse2 + ;; + esac + ac_webrtc_aec3_cflags+=" -DWEBRTC_IOS=1" + ;; + *android*) + case $TARGET_ABI in + armeabi-v7a) + ac_webrtc_aec3_instset=neon + ac_webrtc_aec3_cflags="-mfloat-abi=softfp -mfpu=neon" + ;; + armeabi) + ac_webrtc_aec3_instset=neon + ac_webrtc_aec3_cflags="-mthumb -mfloat-abi=softfp -mfpu=neon -march=armv7" + ;; + arm64*) + ac_webrtc_aec3_instset=neon + ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" + ;; + x86*) + ac_webrtc_aec3_instset=sse2 + ;; + esac + ac_webrtc_aec3_cflags+=" -DWEBRTC_ANDROID=1" + ;; + *mingw* | *cygw*) + ac_webrtc_aec3_instset=sse2 + ac_webrtc_aec3_cflags="-msse2" + ;; + *win32* | *w32* | *darwin* | *linux*) + case $target in + armv7l*gnueabihf) + ac_webrtc_aec3_instset=neon + ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" + ;; + arm-apple-darwin* | aarch64*) + ac_webrtc_aec3_instset=neon + ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" + ;; + *) + ac_webrtc_aec3_instset=sse2 + ;; + esac + case $target in + *darwin*) + ac_webrtc_aec3_cflags+=" -DWEBRTC_MAC=1" + ;; + *linux*) + ac_webrtc_aec3_cflags+=" -DWEBRTC_LINUX=1" + ;; + esac + ;; + *) + ;; + esac + ac_webrtc_aec3_cflags+=" -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_POSIX=1" + else + ac_no_webrtc_aec3=1 + printf "%s\n" "#define PJMEDIA_HAS_LIBWEBRTC_AEC3 0" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libwebrtc-aec3 is enabled...no" >&5 printf "%s\n" "Checking if libwebrtc-aec3 is enabled...no" >&6; } - fi + fi else $as_nop - ac_no_webrtc_aec3=1 - printf "%s\n" "#define PJMEDIA_HAS_LIBWEBRTC_AEC3 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libwebrtc-aec3 is enabled...no" >&5 + ac_no_webrtc_aec3=1 + printf "%s\n" "#define PJMEDIA_HAS_LIBWEBRTC_AEC3 0" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libwebrtc-aec3 is enabled...no" >&5 printf "%s\n" "Checking if libwebrtc-aec3 is enabled...no" >&6; } -fi +fi @@ -10505,99 +10780,107 @@ fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if select() needs correct nfds" >&5 printf %s "checking if select() needs correct nfds... " >&6; } case $target in - *rtems*) printf "%s\n" "#define PJ_SELECT_NEEDS_NFDS 1" >>confdefs.h + *rtems*) + printf "%s\n" "#define PJ_SELECT_NEEDS_NFDS 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - ;; - *) printf "%s\n" "#define PJ_SELECT_NEEDS_NFDS 0" >>confdefs.h + ;; + *) + printf "%s\n" "#define PJ_SELECT_NEEDS_NFDS 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no (default)" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no (default)" >&5 printf "%s\n" "no (default)" >&6; } - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ** Decided that select() doesn't need correct nfds (please check)" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ** Decided that select() doesn't need correct nfds (please check)" >&5 printf "%s\n" "** Decided that select() doesn't need correct nfds (please check)" >&6; } - ;; + ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if pj_thread_create() should enforce stack size" >&5 printf %s "checking if pj_thread_create() should enforce stack size... " >&6; } case $target in - *rtems*) printf "%s\n" "#define PJ_THREAD_SET_STACK_SIZE 1" >>confdefs.h + *rtems*) + printf "%s\n" "#define PJ_THREAD_SET_STACK_SIZE 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - ;; - *) printf "%s\n" "#define PJ_THREAD_SET_STACK_SIZE 0" >>confdefs.h + ;; + *) + printf "%s\n" "#define PJ_THREAD_SET_STACK_SIZE 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no (default)" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no (default)" >&5 printf "%s\n" "no (default)" >&6; } - ;; + ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if pj_thread_create() should allocate stack" >&5 printf %s "checking if pj_thread_create() should allocate stack... " >&6; } case $target in - *rtems*) printf "%s\n" "#define PJ_THREAD_ALLOCATE_STACK 1" >>confdefs.h + *rtems*) + printf "%s\n" "#define PJ_THREAD_ALLOCATE_STACK 1" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - ;; - *) printf "%s\n" "#define PJ_THREAD_ALLOCATE_STACK 0" >>confdefs.h + ;; + *) + printf "%s\n" "#define PJ_THREAD_ALLOCATE_STACK 0" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no (default)" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no (default)" >&5 printf "%s\n" "no (default)" >&6; } - ;; + ;; esac case $target in - *mingw* | *cygw* | *win32* | *w32* ) - printf "%s\n" "#define PJ_BLOCKING_ERROR_VAL WSAEWOULDBLOCK" >>confdefs.h + *mingw* | *cygw* | *win32* | *w32* ) + printf "%s\n" "#define PJ_BLOCKING_ERROR_VAL WSAEWOULDBLOCK" >>confdefs.h - ;; - *) printf "%s\n" "#define PJ_BLOCKING_ERROR_VAL EAGAIN" >>confdefs.h + ;; + *) + printf "%s\n" "#define PJ_BLOCKING_ERROR_VAL EAGAIN" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ** Setting non-blocking recv() retval to EAGAIN (please check)" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ** Setting non-blocking recv() retval to EAGAIN (please check)" >&5 printf "%s\n" "** Setting non-blocking recv() retval to EAGAIN (please check)" >&6; } - ;; + ;; esac case $target in - *mingw* | *cygw* | *win32* | *w32* ) - printf "%s\n" "#define PJ_BLOCKING_CONNECT_ERROR_VAL WSAEWOULDBLOCK" >>confdefs.h + *mingw* | *cygw* | *win32* | *w32* ) + printf "%s\n" "#define PJ_BLOCKING_CONNECT_ERROR_VAL WSAEWOULDBLOCK" >>confdefs.h - ;; - *) printf "%s\n" "#define PJ_BLOCKING_CONNECT_ERROR_VAL EINPROGRESS" >>confdefs.h + ;; + *) + printf "%s\n" "#define PJ_BLOCKING_CONNECT_ERROR_VAL EINPROGRESS" >>confdefs.h - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ** Setting non-blocking connect() retval to EINPROGRESS (please check)" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ** Setting non-blocking connect() retval to EINPROGRESS (please check)" >&5 printf "%s\n" "** Setting non-blocking connect() retval to EINPROGRESS (please check)" >&6; } - ;; + ;; esac case $target in - *linux* | *darwin* | *bsd*) - ac_fn_c_check_header_compile "$LINENO" "pthread_np.h" "ac_cv_header_pthread_np_h" "$ac_includes_default" + *linux* | *darwin* | *bsd*) + ac_fn_c_check_header_compile "$LINENO" "pthread_np.h" "ac_cv_header_pthread_np_h" "$ac_includes_default" if test "x$ac_cv_header_pthread_np_h" = xyes then : printf "%s\n" "#define PJ_HAS_PTHREAD_NP_H 1" >>confdefs.h fi - ac_fn_c_check_func "$LINENO" "pthread_setname_np" "ac_cv_func_pthread_setname_np" + ac_fn_c_check_func "$LINENO" "pthread_setname_np" "ac_cv_func_pthread_setname_np" if test "x$ac_cv_func_pthread_setname_np" = xyes then : printf "%s\n" "#define PJ_HAS_PTHREAD_SETNAME_NP 1" >>confdefs.h fi - ac_fn_c_check_func "$LINENO" "pthread_set_name_np" "ac_cv_func_pthread_set_name_np" + ac_fn_c_check_func "$LINENO" "pthread_set_name_np" "ac_cv_func_pthread_set_name_np" if test "x$ac_cv_func_pthread_set_name_np" = xyes then : printf "%s\n" "#define PJ_HAS_PTHREAD_SET_NAME_NP 1" >>confdefs.h fi - ;; + ;; esac @@ -10606,12 +10889,12 @@ ac_host=unix case $target in - *rtems*) - ac_main_obj=main_rtems.o - ;; - *) - ac_main_obj=main.o - ;; + *rtems*) + ac_main_obj=main_rtems.o + ;; + *) + ac_main_obj=main.o + ;; esac @@ -11896,7 +12179,6 @@ printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Configurations for current target have been written to 'build.mak', and 'os-auto.mak' in various build directories, and pjlib/include/pj/compat/os_auto.h. diff --git a/aconfigure.ac b/aconfigure.ac index df91fbe0e2..7fd65aac20 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -5,28 +5,30 @@ host_orig="$host" AC_CANONICAL_BUILD AC_CANONICAL_HOST AC_CANONICAL_TARGET -AC_CONFIG_HEADERS([pjlib/include/pj/compat/os_auto.h - pjlib/include/pj/compat/m_auto.h - pjmedia/include/pjmedia/config_auto.h - pjmedia/include/pjmedia-codec/config_auto.h - pjsip/include/pjsip/sip_autoconf.h - ]) -AC_CONFIG_FILES([build.mak - build/os-auto.mak - build/cc-auto.mak - pjlib/build/os-auto.mak - pjlib-util/build/os-auto.mak - pjmedia/build/os-auto.mak - pjsip/build/os-auto.mak - third_party/build/os-auto.mak - ]) +AC_CONFIG_HEADERS([ + pjlib/include/pj/compat/os_auto.h + pjlib/include/pj/compat/m_auto.h + pjmedia/include/pjmedia/config_auto.h + pjmedia/include/pjmedia-codec/config_auto.h + pjsip/include/pjsip/sip_autoconf.h +]) +AC_CONFIG_FILES([ + build.mak + build/os-auto.mak + build/cc-auto.mak + pjlib/build/os-auto.mak + pjlib-util/build/os-auto.mak + pjmedia/build/os-auto.mak + pjsip/build/os-auto.mak + third_party/build/os-auto.mak +]) dnl dnl Setup default CFLAGS dnl if test "$CFLAGS" = ""; then - CFLAGS="-O2" + CFLAGS="-O2" fi dnl # @@ -71,31 +73,31 @@ AC_SUBST(ac_pjdir) AC_SUBST(ac_build_mak_vars) AC_SUBST(ac_cflags) -case $host in +case $host in *mingw* | *cygw* | *win32* | *w32* ) - if pwd -W 2&> /dev/null; then - ac_pjdir=`pwd -W` - else - # We're probably cross-compiling mingw on Linux - ac_pjdir=`pwd` - fi - ;; + if pwd -W 2&> /dev/null; then + ac_pjdir=`pwd -W` + else + # We're probably cross-compiling mingw on Linux + ac_pjdir=`pwd` + fi + ;; *) - ac_pjdir=`pwd` - ;; + ac_pjdir=`pwd` + ;; esac AC_SUBST(ac_shlib_suffix) case $target in *mingw* | *cygw* | *win32* | *w32* ) - ac_shlib_suffix=dll - ;; + ac_shlib_suffix=dll + ;; *darwin*) - ac_shlib_suffix=dylib - ;; + ac_shlib_suffix=dylib + ;; *) - ac_shlib_suffix=so - ;; + ac_shlib_suffix=so + ;; esac AC_SUBST(ac_cross_compile) @@ -118,16 +120,18 @@ dnl dnl libuuid dnl AC_ARG_ENABLE(libuuid, - AS_HELP_STRING([--disable-libuuid], - [Exclude libuuid(default: autodetect)]), - [if test "$enable_libuuid" = "no"; then - AC_MSG_RESULT([Checking if libuuid disabled... yes]) - ac_has_uuid_lib=0 - fi], - [ - AC_CHECK_LIB(uuid,uuid_generate) - AC_CHECK_LIB(uuid,uuid_generate,[ac_has_uuid_lib=1]) - ]) + AS_HELP_STRING([--disable-libuuid], [Exclude libuuid(default: autodetect)]), + [ + if test "$enable_libuuid" = "no"; then + AC_MSG_RESULT([Checking if libuuid disabled... yes]) + ac_has_uuid_lib=0 + fi + ], + [ + AC_CHECK_LIB(uuid,uuid_generate) + AC_CHECK_LIB(uuid,uuid_generate,[ac_has_uuid_lib=1]) + ] +) AC_SEARCH_LIBS(gethostbyname,nsl) @@ -140,13 +144,13 @@ dnl AC_MSG_CHECKING([memory alignment]) case $target in sparc64-* | ia64-* | x86_64-* | arm64-* | aarch64-* | mips64* ) - AC_DEFINE(PJ_POOL_ALIGNMENT, 8) - AC_MSG_RESULT([8 bytes]) - ;; + AC_DEFINE(PJ_POOL_ALIGNMENT, 8) + AC_MSG_RESULT([8 bytes]) + ;; * ) - AC_DEFINE(PJ_POOL_ALIGNMENT, 4) - AC_MSG_RESULT([4 bytes (default)]) - ;; + AC_DEFINE(PJ_POOL_ALIGNMENT, 4) + AC_MSG_RESULT([4 bytes (default)]) + ;; esac @@ -156,11 +160,11 @@ dnl AC_C_BIGENDIAN if test "x$ac_cv_c_bigendian" = "xyes"; then - CFLAGS="$CFLAGS -DPJ_IS_BIG_ENDIAN=1 -DPJ_IS_LITTLE_ENDIAN=0" - ac_cflags="$ac_cflags -DPJ_IS_BIG_ENDIAN=1 -DPJ_IS_LITTLE_ENDIAN=0" + CFLAGS="$CFLAGS -DPJ_IS_BIG_ENDIAN=1 -DPJ_IS_LITTLE_ENDIAN=0" + ac_cflags="$ac_cflags -DPJ_IS_BIG_ENDIAN=1 -DPJ_IS_LITTLE_ENDIAN=0" else - CFLAGS="$CFLAGS -DPJ_IS_BIG_ENDIAN=0 -DPJ_IS_LITTLE_ENDIAN=1" - ac_cflags="$ac_cflags -DPJ_IS_BIG_ENDIAN=0 -DPJ_IS_LITTLE_ENDIAN=1" + CFLAGS="$CFLAGS -DPJ_IS_BIG_ENDIAN=0 -DPJ_IS_LITTLE_ENDIAN=1" + ac_cflags="$ac_cflags -DPJ_IS_BIG_ENDIAN=0 -DPJ_IS_LITTLE_ENDIAN=1" fi dnl @@ -170,52 +174,54 @@ AC_SUBST(ac_target_arch) AC_SUBST(ac_std_cpp_lib) case $target in *android*) - AC_DEFINE(PJ_ANDROID,1) - ac_target_arch=$TARGET_ABI - ac_std_cpp_lib=$STD_CPP_LIB - ;; + AC_DEFINE(PJ_ANDROID,1) + ac_target_arch=$TARGET_ABI + ac_std_cpp_lib=$STD_CPP_LIB + ;; *mingw* | *cygw* | *win32* | *w32* ) - AC_DEFINE(PJ_WIN32,1) - AC_DEFINE(PJ_WIN32_WINNT,0x0400) - AC_DEFINE(WIN32_LEAN_AND_MEAN) - case $target in - *_64-w64-mingw* ) - AC_DEFINE(PJ_WIN64,1) - ;; - esac - ;; + AC_DEFINE(PJ_WIN32,1) + AC_DEFINE(PJ_WIN32_WINNT,0x0400) + AC_DEFINE(WIN32_LEAN_AND_MEAN) + case $target in + *_64-w64-mingw* ) + AC_DEFINE(PJ_WIN64,1) + ;; + esac + ;; *darwin*) - AC_DEFINE(PJ_DARWINOS,1) - ac_target_arch=$ARCH_VAL - ;; + AC_DEFINE(PJ_DARWINOS,1) + ac_target_arch=$ARCH_VAL + ;; *linux*) - AC_DEFINE(PJ_LINUX,1) - ;; + AC_DEFINE(PJ_LINUX,1) + ;; *bsd*) - AC_DEFINE(PJ_BSD,1) - ;; + AC_DEFINE(PJ_BSD,1) + ;; *rtems*) - AC_DEFINE(PJ_RTEMS,1) - ;; + AC_DEFINE(PJ_RTEMS,1) + ;; *sunos* | *solaris* ) - AC_DEFINE(PJ_SUNOS,1) - ;; + AC_DEFINE(PJ_SUNOS,1) + ;; *) - ;; + ;; esac dnl # --disable-floating-point option AC_ARG_ENABLE(floating-point, - AS_HELP_STRING([--disable-floating-point], - [Disable floating point where possible]), - [if test "$enable_floating_point" = "no"; then - AC_DEFINE(PJ_HAS_FLOATING_POINT,0) - AC_MSG_RESULT([Checking if floating point is disabled... yes]) - fi], - [ - AC_DEFINE(PJ_HAS_FLOATING_POINT,1) - AC_MSG_RESULT([Checking if floating point is disabled... no]) - ]) + AS_HELP_STRING([--disable-floating-point], [Disable floating point where possible]), + [ + if test "$enable_floating_point" = "no"; then + AC_DEFINE(PJ_HAS_FLOATING_POINT,0) + AC_MSG_RESULT([Checking if floating point is disabled... yes]) + fi + ], + [ + AC_DEFINE(PJ_HAS_FLOATING_POINT,1) + AC_MSG_RESULT([Checking if floating point is disabled... no]) + ] +) AC_CHECK_HEADER(arpa/inet.h,[AC_DEFINE(PJ_HAS_ARPA_INET_H,1)]) @@ -224,11 +230,11 @@ AC_CHECK_HEADER(ctype.h,[AC_DEFINE(PJ_HAS_CTYPE_H,1)]) case $target in *mingw* | *cygw* | *win32* | *w32* ) - AC_DEFINE(PJ_HAS_ERRNO_H,0) - ;; + AC_DEFINE(PJ_HAS_ERRNO_H,0) + ;; *) - AC_CHECK_HEADER(errno.h,[AC_DEFINE(PJ_HAS_ERRNO_H,1)]) - ;; + AC_CHECK_HEADER(errno.h,[AC_DEFINE(PJ_HAS_ERRNO_H,1)]) + ;; esac AC_CHECK_HEADER(fcntl.h,[AC_DEFINE(PJ_HAS_FCNTL_H,1)]) @@ -239,19 +245,21 @@ AC_CHECK_HEADER(netdb.h,[AC_DEFINE(PJ_HAS_NETDB_H,1)]) AC_CHECK_HEADER(netinet/in_systm.h,[AC_DEFINE(PJ_HAS_NETINET_IN_SYSTM_H,1)]) AC_CHECK_HEADER(netinet/in.h,[AC_DEFINE(PJ_HAS_NETINET_IN_H,1)]) AC_CHECK_HEADER(netinet/ip.h,[AC_DEFINE(PJ_HAS_NETINET_IP_H,1)],[], - [#if PJ_HAS_SYS_TYPES_H - # include - #endif - #if PJ_HAS_NETINET_IN_SYSTM_H - # include - #endif - #if PJ_HAS_NETINET_IN_H - # include - #endif - ]) + [ + #if PJ_HAS_SYS_TYPES_H + # include + #endif + #if PJ_HAS_NETINET_IN_SYSTM_H + # include + #endif + #if PJ_HAS_NETINET_IN_H + # include + #endif + ] +) + AC_CHECK_HEADER(netinet/tcp.h,[AC_DEFINE(PJ_HAS_NETINET_TCP_H,1)]) -AC_CHECK_HEADER(ifaddrs.h, - [AC_CHECK_FUNC(getifaddrs,[AC_DEFINE(PJ_HAS_IFADDRS_H,1)])]) +AC_CHECK_HEADER(ifaddrs.h, [AC_CHECK_FUNC(getifaddrs,[AC_DEFINE(PJ_HAS_IFADDRS_H,1)])]) AC_CHECK_HEADER(semaphore.h,[AC_DEFINE(PJ_HAS_SEMAPHORE_H,1)]) AC_CHECK_HEADER(setjmp.h,[AC_DEFINE(PJ_HAS_SETJMP_H,1)]) AC_CHECK_HEADER(stdarg.h,[AC_DEFINE(PJ_HAS_STDARG_H,1)]) @@ -276,28 +284,32 @@ AC_CHECK_HEADER(execinfo.h,[AC_DEFINE(PJ_HAS_EXECINFO_H,1)]) AC_CHECK_HEADER(winsock.h,[AC_DEFINE(PJ_HAS_WINSOCK_H,1)]) AC_CHECK_HEADER(winsock2.h,[AC_DEFINE(PJ_HAS_WINSOCK2_H,1)]) AC_CHECK_HEADER(mswsock.h,[AC_DEFINE(PJ_HAS_MSWSOCK_H,1)],[], - [#if PJ_HAS_WINSOCK2_H - # include - #elif PJ_HAS_WINSOCK_H - # include - #endif - ]) + [ + #if PJ_HAS_WINSOCK2_H + # include + #elif PJ_HAS_WINSOCK_H + # include + #endif + ] +) + AC_CHECK_HEADER(ws2tcpip.h,[AC_DEFINE(PJ_HAS_WS2TCPIP_H,1)]) AC_CHECK_HEADER(uuid/uuid.h,[ac_has_uuid_h=1]) AC_CHECK_HEADER(net/if.h,[AC_DEFINE(PJ_HAS_NET_IF_H,1)],[], - [#if PJ_HAS_SYS_TYPES_H - # include - #endif - #if PJ_HAS_SYS_SOCKET_H - - # include - #endif - ]) + [ + #if PJ_HAS_SYS_TYPES_H + # include + #endif + #if PJ_HAS_SYS_SOCKET_H + # include + #endif + ] +) case $target in - *android*) - AC_CHECK_HEADER(linux/android_alarm.h,[AC_DEFINE(PJ_HAS_ANDROID_ALARM_H,1)]) - ;; + *android*) + AC_CHECK_HEADER(linux/android_alarm.h,[AC_DEFINE(PJ_HAS_ANDROID_ALARM_H,1)]) + ;; esac AC_CHECK_FUNC(localtime_r,[AC_DEFINE(PJ_HAS_LOCALTIME_R,1)]) @@ -322,147 +334,239 @@ AC_DEFINE(PJ_ATOMIC_VALUE_TYPE,long) dnl # Determine if inet_aton() is available AC_MSG_CHECKING([if inet_aton() is available]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include - #include - #include ]], - [inet_aton(0, 0);])], - [AC_DEFINE(PJ_SOCK_HAS_INET_ATON,1) - AC_MSG_RESULT(yes)], - [AC_MSG_RESULT(no)]) +AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include + #include + #include ]], + [inet_aton(0, 0);]) + ], + [ + AC_DEFINE(PJ_SOCK_HAS_INET_ATON,1) + AC_MSG_RESULT(yes) + ], + [AC_MSG_RESULT(no)] +) dnl # Determine if inet_pton() is available AC_MSG_CHECKING([if inet_pton() is available]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include - #include - #include ]], - [inet_pton(0, 0, 0);])], - [AC_DEFINE(PJ_SOCK_HAS_INET_PTON,1) - AC_MSG_RESULT(yes)], - [AC_MSG_RESULT(no)]) +AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include + #include + #include ]], + [inet_pton(0, 0, 0);]) + ], + [ + AC_DEFINE(PJ_SOCK_HAS_INET_PTON,1) + AC_MSG_RESULT(yes) + ], + [AC_MSG_RESULT(no)] +) dnl # Determine if inet_ntop() is available AC_MSG_CHECKING([if inet_ntop() is available]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include - #include - #include ]], - [inet_ntop(0, 0, 0, 0);])], - [AC_DEFINE(PJ_SOCK_HAS_INET_NTOP,1) - AC_MSG_RESULT(yes)], - [AC_MSG_RESULT(no)]) +AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include + #include + #include ]], + [inet_ntop(0, 0, 0, 0);]) + ], + [ + AC_DEFINE(PJ_SOCK_HAS_INET_NTOP,1) + AC_MSG_RESULT(yes) + ], + [AC_MSG_RESULT(no)] +) dnl # Determine if getaddrinfo() is available AC_MSG_CHECKING([if getaddrinfo() is available]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include - #include - #include ]], - [getaddrinfo(0, 0, 0, 0);])], - [AC_DEFINE(PJ_SOCK_HAS_GETADDRINFO,1) - AC_MSG_RESULT(yes)], - [AC_MSG_RESULT(no)]) +AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include + #include + #include ]], + [getaddrinfo(0, 0, 0, 0);]) + ], + [ + AC_DEFINE(PJ_SOCK_HAS_GETADDRINFO,1) + AC_MSG_RESULT(yes) + ], + [AC_MSG_RESULT(no)] +) dnl # Determine if socketpair() is available AC_MSG_CHECKING([if socketpair() is available]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include - #include ]], - [socketpair(0, 0, 0, 0);])], - [AC_DEFINE(PJ_SOCK_HAS_SOCKETPAIR,1) - AC_MSG_RESULT(yes)], - [AC_MSG_RESULT(no)]) +AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include + #include ]], + [socketpair(0, 0, 0, 0);]) + ], + [ + AC_DEFINE(PJ_SOCK_HAS_SOCKETPAIR,1) + AC_MSG_RESULT(yes) + ], + [AC_MSG_RESULT(no)] +) dnl # Determine if sockaddr_in has sin_len member AC_MSG_CHECKING([if sockaddr_in has sin_len member]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include - #include - #include - #include ]], - [struct sockaddr_in a; a.sin_len=0;])], - [AC_DEFINE(PJ_SOCKADDR_HAS_LEN,1) - AC_MSG_RESULT(yes)], - AC_MSG_RESULT(no)) +AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include + #include + #include + #include ]], + [struct sockaddr_in a; a.sin_len=0;]) + ], + [ + AC_DEFINE(PJ_SOCKADDR_HAS_LEN,1) + AC_MSG_RESULT(yes) + ], + [AC_MSG_RESULT(no)] +) dnl # Determine if socklen_t is available AC_MSG_CHECKING([if socklen_t is available]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include - #include ]], - [socklen_t xxx = 0;])], - [AC_DEFINE(PJ_HAS_SOCKLEN_T,1) - AC_MSG_RESULT(yes)], - AC_MSG_RESULT(no)) +AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include + #include ]], + [socklen_t xxx = 0;]) + ], + [ + AC_DEFINE(PJ_HAS_SOCKLEN_T,1) + AC_MSG_RESULT(yes) + ], + [AC_MSG_RESULT(no)] +) dnl # Determine if IPV6_V6ONLY is available AC_MSG_CHECKING([if IPV6_V6ONLY is available]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include - #include ]], - [int opt = IPV6_V6ONLY;])], - [AC_DEFINE(PJ_SOCK_HAS_IPV6_V6ONLY,1) - AC_MSG_RESULT(yes)], - AC_MSG_RESULT(no)) +AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include + #include ]], + [int opt = IPV6_V6ONLY;]) + ], + [ + AC_DEFINE(PJ_SOCK_HAS_IPV6_V6ONLY,1) + AC_MSG_RESULT(yes) + ], + [AC_MSG_RESULT(no)] +) dnl # Determine if SO_ERROR is available AC_MSG_CHECKING([if SO_ERROR is available]) case $target in *mingw* | *cygw* | *win32* | *w32* ) - AC_DEFINE(PJ_HAS_SO_ERROR,1) - AC_MSG_RESULT(yes) - ;; + AC_DEFINE(PJ_HAS_SO_ERROR,1) + AC_MSG_RESULT(yes) + ;; *) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include - #include - #include - #include ]], - [int i=SO_ERROR;])], - [AC_DEFINE(PJ_HAS_SO_ERROR,1) - AC_MSG_RESULT(yes)], - AC_MSG_RESULT(no)) - ;; + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include + #include + #include + #include ]], + [int i=SO_ERROR;]) + ], + [ + AC_DEFINE(PJ_HAS_SO_ERROR,1) + AC_MSG_RESULT(yes) + ], + [AC_MSG_RESULT(no)] + ) + ;; esac dnl # Determine if RW-mutex is available AC_MSG_CHECKING([if pthread_rwlock_t is available]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ], - [pthread_rwlock_t *x;])], - [AC_DEFINE(PJ_EMULATE_RWMUTEX,0) - ac_rwmutex="yes" - AC_MSG_RESULT(yes)], - [AC_DEFINE(PJ_EMULATE_RWMUTEX,1) - ac_rwmutex="no" - AC_MSG_RESULT(no)]) +AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([#include ],[pthread_rwlock_t *x;]) + ], + [ + AC_DEFINE(PJ_EMULATE_RWMUTEX,0) + ac_rwmutex="yes" + AC_MSG_RESULT(yes) + ], + [ + AC_DEFINE(PJ_EMULATE_RWMUTEX,1) + ac_rwmutex="no" + AC_MSG_RESULT(no) + ] +) dnl # If rwmutex is not detected, check again but this time dnl # with _POSIX_READER_WRITER_LOCKS defined (newlib needs this) if test "$ac_rwmutex" = "no"; then AC_MSG_CHECKING([if pthread_rwlock_t is available with _POSIX_READER_WRITER_LOCKS]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#define _POSIX_READER_WRITER_LOCKS - #include ]], - [pthread_rwlock_t *x;])], - [AC_DEFINE(PJ_EMULATE_RWMUTEX,0) - CFLAGS="$CFLAGS -D_POSIX_THREADS -D_POSIX_READER_WRITER_LOCKS" - AC_MSG_RESULT(yes)], - [AC_DEFINE(PJ_EMULATE_RWMUTEX,1) - AC_MSG_RESULT(no)]) + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#define _POSIX_READER_WRITER_LOCKS + #include ]], + [pthread_rwlock_t *x;]) + ], + [ + AC_DEFINE(PJ_EMULATE_RWMUTEX,0) + CFLAGS="$CFLAGS -D_POSIX_THREADS -D_POSIX_READER_WRITER_LOCKS" + AC_MSG_RESULT(yes) + ], + [ + AC_DEFINE(PJ_EMULATE_RWMUTEX,1) + AC_MSG_RESULT(no) + ] + ) fi dnl # Do we have pthread_mutexattr_settype()? AC_MSG_CHECKING([if pthread_mutexattr_settype() is available]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ], - [pthread_mutexattr_settype(0,PTHREAD_MUTEX_FAST_NP);])], - [AC_DEFINE(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE,1) - AC_MSG_RESULT(yes)], - [AC_MSG_RESULT(no)]) +AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM( + [#include ], + [pthread_mutexattr_settype(0,PTHREAD_MUTEX_FAST_NP);]) + ], + [ + AC_DEFINE(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE,1) + AC_MSG_RESULT(yes) + ], + [AC_MSG_RESULT(no)] +) dnl # Does pthread_mutexattr_t has "recursive" member? AC_MSG_CHECKING([if pthread_mutexattr_t has recursive member]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ], - [[pthread_mutexattr_t attr; - attr.recursive=1;]])], - [AC_DEFINE(PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE,1) - AC_MSG_RESULT(yes)], - [AC_MSG_RESULT(no)]) +AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM( + [#include ], + [pthread_mutexattr_t attr; attr.recursive=1;]) + ], + [ + AC_DEFINE(PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE,1) + AC_MSG_RESULT(yes) + ], + [AC_MSG_RESULT(no)] +) dnl ###################### dnl # ioqueue selection -dnl # +dnl # AC_SUBST(ac_os_objs) AC_SUBST(ac_linux_poll) AC_MSG_CHECKING([ioqueue backend]) @@ -471,132 +575,135 @@ ac_os_objs=ioqueue_select.o ac_linux_poll=select case $target in - *darwin* | *bsd*) - AC_ARG_ENABLE(kqueue, - AS_HELP_STRING([--enable-kqueue], - [Use kqueue ioqueue on macos/BSD (experimental)]), - [ - if test "$enable_kqueue" = "yes"; then - ac_os_objs=ioqueue_kqueue.o - AC_MSG_RESULT([kqueue()]) - else - AC_MSG_RESULT([select()]) - fi - ], - [ - AC_MSG_RESULT([select()]) - ]) - ;; - *) - AC_ARG_ENABLE(epoll, - AS_HELP_STRING([--enable-epoll], - [Use /dev/epoll ioqueue on Linux (experimental)]), - [ - if test "$enable_epoll" = "yes"; then - ac_os_objs=ioqueue_epoll.o - AC_MSG_RESULT([/dev/epoll]) - AC_DEFINE(PJ_HAS_LINUX_EPOLL,1) - ac_linux_poll=epoll - else - AC_MSG_RESULT([select()]) - fi - ], - [ - AC_MSG_RESULT([select()]) - ]) - ;; + *darwin* | *bsd*) + AC_ARG_ENABLE(kqueue, + AS_HELP_STRING([--enable-kqueue], [Use kqueue ioqueue on macos/BSD (experimental)]), + [ + if test "$enable_kqueue" = "yes"; then + ac_os_objs=ioqueue_kqueue.o + AC_MSG_RESULT([kqueue()]) + else + AC_MSG_RESULT([select()]) + fi + ], + [ + AC_MSG_RESULT([select()]) + ] + ) + ;; + *) + AC_ARG_ENABLE(epoll, + AS_HELP_STRING([--enable-epoll], [Use /dev/epoll ioqueue on Linux (experimental)]), + [ + if test "$enable_epoll" = "yes"; then + ac_os_objs=ioqueue_epoll.o + AC_MSG_RESULT([/dev/epoll]) + AC_DEFINE(PJ_HAS_LINUX_EPOLL,1) + ac_linux_poll=epoll + else + AC_MSG_RESULT([select()]) + fi + ], + [ + AC_MSG_RESULT([select()]) + ] + ) + ;; esac AC_SUBST(ac_shared_libraries) AC_ARG_ENABLE(shared, - AS_HELP_STRING([--enable-shared], - [Build shared libraries]), - [if test "$enable_shared" = "yes"; then - [ac_shared_libraries=1] - CFLAGS="$CFLAGS -fPIC" - AC_MSG_RESULT([Building shared libraries... yes]) - fi], - AC_MSG_RESULT([Building shared libraries... no]) - ) + AS_HELP_STRING([--enable-shared], [Build shared libraries]), + [ + if test "$enable_shared" = "yes"; then + [ac_shared_libraries=1] + CFLAGS="$CFLAGS -fPIC" + AC_MSG_RESULT([Building shared libraries... yes]) + fi + ], + AC_MSG_RESULT([Building shared libraries... no]) +) AC_SUBST(ac_no_pjsua2) AC_ARG_ENABLE(pjsua2, - AS_HELP_STRING([--disable-pjsua2], - [Exclude pjsua2 library and application from the build]), - [if test "$enable_pjsua2" = "no"; then - [ac_no_pjsua2=1] - AC_MSG_RESULT([Building pjsua2 library and application... no]) - fi], - AC_MSG_RESULT([Building pjsua2 library and application... yes]) - ) + AS_HELP_STRING([--disable-pjsua2], [Exclude pjsua2 library and application from the build]), + [ + if test "$enable_pjsua2" = "no"; then + [ac_no_pjsua2=1] + AC_MSG_RESULT([Building pjsua2 library and application... no]) + fi + ], + AC_MSG_RESULT([Building pjsua2 library and application... yes]) +) dnl ###################### dnl # OS specific files dnl # case $target in - *mingw* | *cygw* | *win32* | *w32* ) - ac_os_objs="$ac_os_objs file_access_win32.o file_io_win32.o os_core_win32.o os_error_win32.o os_time_win32.o os_timestamp_win32.o guid_win32.o sock_qos_bsd.o unicode_win32.o" - ;; - *) - ac_os_objs="$ac_os_objs file_access_unistd.o file_io_ansi.o os_core_unix.o os_error_unix.o os_time_unix.o os_timestamp_posix.o" - case $target in - *-apple-darwin_ios*) - ac_os_objs="$ac_os_objs os_info_iphone.o os_core_darwin.o" - ;; - *darwin*) - ac_os_objs="$ac_os_objs os_core_darwin.o" - ;; - esac - # QoS - case $target in - *darwin*) - ac_os_objs="$ac_os_objs sock_qos_darwin.o sock_qos_bsd.o" - ;; - *) - ac_os_objs="$ac_os_objs sock_qos_bsd.o" - ;; - esac - # SSL - case $target in - *darwin*) - ac_os_objs="$ac_os_objs ssl_sock_apple.o" - ;; - esac - # UUID - case $target in - *android*) - ac_os_objs="$ac_os_objs guid_android.o" - ;; - *darwin*) - ac_os_objs="$ac_os_objs guid_darwin.o" - ;; - *bsd*) - ac_os_objs="$ac_os_objs guid_bsd.o" - ;; - *) - if test "$ac_has_uuid_lib" = "1" -a "$ac_has_uuid_h" = "1"; then - ac_os_objs="$ac_os_objs guid_uuid.o" - else - ac_os_objs="$ac_os_objs guid_simple.o" - fi - ;; - esac - ;; + *mingw* | *cygw* | *win32* | *w32* ) + ac_os_objs="$ac_os_objs file_access_win32.o file_io_win32.o os_core_win32.o os_error_win32.o os_time_win32.o os_timestamp_win32.o guid_win32.o sock_qos_bsd.o unicode_win32.o" + ;; + *) + ac_os_objs="$ac_os_objs file_access_unistd.o file_io_ansi.o os_core_unix.o os_error_unix.o os_time_unix.o os_timestamp_posix.o" + case $target in + *-apple-darwin_ios*) + ac_os_objs="$ac_os_objs os_info_iphone.o os_core_darwin.o" + ;; + *darwin*) + ac_os_objs="$ac_os_objs os_core_darwin.o" + ;; + esac + + # QoS + case $target in + *darwin*) + ac_os_objs="$ac_os_objs sock_qos_darwin.o sock_qos_bsd.o" + ;; + *) + ac_os_objs="$ac_os_objs sock_qos_bsd.o" + ;; + esac + + # SSL + case $target in + *darwin*) + ac_os_objs="$ac_os_objs ssl_sock_apple.o" + ;; + esac + + # UUID + case $target in + *android*) + ac_os_objs="$ac_os_objs guid_android.o" + ;; + *darwin*) + ac_os_objs="$ac_os_objs guid_darwin.o" + ;; + *bsd*) + ac_os_objs="$ac_os_objs guid_bsd.o" + ;; + *) + if test "$ac_has_uuid_lib" = "1" -a "$ac_has_uuid_h" = "1"; then + ac_os_objs="$ac_os_objs guid_uuid.o" + else + ac_os_objs="$ac_os_objs guid_simple.o" + fi + ;; + esac + ;; esac dnl ########################################## dnl # PJLIB-UTIL dnl # - dnl # UPnP alt prefix AC_ARG_WITH(upnp, - AS_HELP_STRING([--with-upnp=DIR], - [Specify alternate libupnp prefix]), - [], - [with_upnp=no] - ) + AS_HELP_STRING([--with-upnp=DIR], [Specify alternate libupnp prefix]), + [], + [with_upnp=no] +) dnl # Do not use default libupnp installation if we are cross-compiling if test "x$ac_cross_compile" != "x" -a "x$with_upnp" = "xno"; then @@ -605,51 +712,55 @@ fi dnl # UPnP AC_ARG_ENABLE(upnp, - AS_HELP_STRING([--disable-upnp], - [Disable UPnP (default: not disabled)]), - [ - if test "$enable_upnp" = "no"; then - AC_MSG_RESULT([Checking if UPnP is disabled... yes]) - fi - ], - [ - if test "x$with_upnp" != "xno" -a "x$with_upnp" != "x"; then - UPNP_PREFIX=$with_upnp - UPNP_CFLAGS="-I$UPNP_PREFIX/upnp/inc -I$UPNP_PREFIX/ixml/inc" - UPNP_LDFLAGS="-L$UPNP_PREFIX/upnp/.libs -L$UPNP_PREFIX/imxl/.libs" - AC_MSG_RESULT([Using UPnP prefix... $with_upnp]) - else - UPNP_CFLAGS="" - UPNP_LDFLAGS="" - fi - - AC_MSG_CHECKING([UPnP usability]) - - UPNP_LIBS="-lupnp -lixml" - - SAVED_LIBS="$LIBS" - SAVED_LDFLAGS="$LDFLAGS" - SAVED_CFLAGS="$CFLAGS" - - LIBS="$UPNP_LIBS $LIBS" - LDFLAGS="$UPNP_LDFLAGS $LDFLAGS" - CFLAGS="$UPNP_CFLAGS $CFLAGS" - - AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], - [UpnpInit2(NULL, 0);] - )], - [ CFLAGS="$CFLAGS -DPJNATH_HAS_UPNP=1 $UPNP_CFLAGS" - LDFLAGS="$LDFLAGS $UPNP_LDFLAGS $UPNP_LIBS" - AC_MSG_RESULT(yes) - ], - [ - LIBS="$SAVED_LIBS" - LDFLAGS="$SAVED_LDFLAGS" - CFLAGS="$SAVED_CFLAGS" - AC_MSG_RESULT(no) - ]) - - ]) + AS_HELP_STRING([--disable-upnp], [Disable UPnP (default: not disabled)]), + [ + if test "$enable_upnp" = "no"; then + AC_MSG_RESULT([Checking if UPnP is disabled... yes]) + fi + ], + [ + if test "x$with_upnp" != "xno" -a "x$with_upnp" != "x"; then + UPNP_PREFIX=$with_upnp + UPNP_CFLAGS="-I$UPNP_PREFIX/upnp/inc -I$UPNP_PREFIX/ixml/inc" + UPNP_LDFLAGS="-L$UPNP_PREFIX/upnp/.libs -L$UPNP_PREFIX/imxl/.libs" + AC_MSG_RESULT([Using UPnP prefix... $with_upnp]) + else + UPNP_CFLAGS="" + UPNP_LDFLAGS="" + fi + + AC_MSG_CHECKING([UPnP usability]) + + UPNP_LIBS="-lupnp -lixml" + + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CFLAGS="$CFLAGS" + + LIBS="$UPNP_LIBS $LIBS" + LDFLAGS="$UPNP_LDFLAGS $LDFLAGS" + CFLAGS="$UPNP_CFLAGS $CFLAGS" + + AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include ]], + [UpnpInit2(NULL, 0);]) + ], + [ + CFLAGS="$CFLAGS -DPJNATH_HAS_UPNP=1 $UPNP_CFLAGS" + LDFLAGS="$LDFLAGS $UPNP_LDFLAGS $UPNP_LIBS" + AC_MSG_RESULT(yes) + ], + [ + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CFLAGS="$SAVED_CFLAGS" + AC_MSG_RESULT(no) + ] + ) + ] +) dnl ########################################## dnl # @@ -660,58 +771,73 @@ dnl # Use external Speex installation AC_SUBST(ac_external_speex,0) AC_ARG_WITH(external-speex, AS_HELP_STRING([--with-external-speex], - [Use external Speex development files, not the one in "third_party" directory. When this option is set, make sure that Speex is accessible to use (hint: use CFLAGS and LDFLAGS env var to set the include/lib paths)]), - [ - if test "x$with_external_speex" != "xno"; then - # Test Speex installation - AC_MSG_CHECKING([if external Speex devkit is installed]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include - #include - ]], - [speex_echo_state_init(0, 0); speex_encoder_init(0); ])], - [AC_MSG_RESULT(yes!!) - AC_DEFINE(PJMEDIA_EXTERNAL_SPEEX_CODEC, 1) - ac_external_speex="1" - ], - [AC_MSG_ERROR([Unable to use external Speex library. If Speex development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths])]) - fi + [Use external Speex development files, not the one in "third_party" directory. When this option is set, make sure that Speex is accessible to use (hint: use CFLAGS and LDFLAGS env var to set the include/lib paths)]), + [ + if test "x$with_external_speex" != "xno"; then + # Test Speex installation + AC_MSG_CHECKING([if external Speex devkit is installed]) + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include + #include ]], + [speex_echo_state_init(0, 0); speex_encoder_init(0); ]) + ], + [ + AC_MSG_RESULT(yes!!) + AC_DEFINE(PJMEDIA_EXTERNAL_SPEEX_CODEC, 1) + ac_external_speex="1" + ], + [ + AC_MSG_ERROR([Unable to use external Speex library. If Speex development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths]) + ] + ) + fi ] - ) +) dnl # Use external GSM codec library installation AC_SUBST(ac_external_gsm,0) AC_ARG_WITH(external-gsm, AS_HELP_STRING([--with-external-gsm], - [Use external GSM codec library, not the one in "third_party" directory. When this option is set, make sure that the GSM include/lib files are accessible to use (hint: use CFLAGS and LDFLAGS env var to set the include/lib paths)]), + [Use external GSM codec library, not the one in "third_party" directory. When this option is set, make sure that the GSM include/lib files are accessible to use (hint: use CFLAGS and LDFLAGS env var to set the include/lib paths)]), [ - if test "x$with_external_gsm" != "xno"; then - # Test GSM library installation - AC_MSG_CHECKING([if external GSM devkit is installed as gsm/gsm.h]) - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM([[#include ]], [gsm_create(); ]) ], - [AC_MSG_RESULT(yes!!) - AC_DEFINE(PJMEDIA_EXTERNAL_GSM_CODEC, 1) - AC_DEFINE(PJMEDIA_EXTERNAL_GSM_GSM_H, 1) - ac_external_gsm="1" - ], - [ - AC_MSG_RESULT(no) - AC_MSG_CHECKING([if external GSM devkit is installed as gsm.h]) - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM([[#include ]], [gsm_create(); ]) ], - [AC_MSG_RESULT(yes!!) - AC_DEFINE(PJMEDIA_EXTERNAL_GSM_CODEC, 1) - AC_DEFINE(PJMEDIA_EXTERNAL_GSM_H, 1) - ac_external_gsm="1" + if test "x$with_external_gsm" != "xno"; then + # Test GSM library installation + AC_MSG_CHECKING([if external GSM devkit is installed as gsm/gsm.h]) + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([[#include ]], [gsm_create(); ]) + ], + [ + AC_MSG_RESULT(yes!!) + AC_DEFINE(PJMEDIA_EXTERNAL_GSM_CODEC, 1) + AC_DEFINE(PJMEDIA_EXTERNAL_GSM_GSM_H, 1) + ac_external_gsm="1" + ], + [ + AC_MSG_RESULT(no) + AC_MSG_CHECKING([if external GSM devkit is installed as gsm.h]) + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include ]], [gsm_create(); ]) + ], + [ + AC_MSG_RESULT(yes!!) + AC_DEFINE(PJMEDIA_EXTERNAL_GSM_CODEC, 1) + AC_DEFINE(PJMEDIA_EXTERNAL_GSM_H, 1) + ac_external_gsm="1" ], - [AC_MSG_ERROR([Unable to use external GSM library. If GSM development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths]) + [ + AC_MSG_ERROR([Unable to use external GSM library. If GSM development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths]) ] ) - ] - ) - fi + ] + ) + fi ] - ) +) dnl # Use external SRTP installation @@ -719,632 +845,718 @@ AC_SUBST(ac_external_srtp,0) AC_SUBST(ac_external_srtp_lib) AC_ARG_WITH(external-srtp, AS_HELP_STRING([--with-external-srtp], - [Use external SRTP development files, not the one in "third_party" directory. When this option is set, make sure that SRTP is accessible to use (hint: use CFLAGS and LDFLAGS env var to set the include/lib paths)]), - [ - if test "x$with_external_srtp" != "xno"; then - # Test SRTP installation - AC_MSG_CHECKING([if external SRTP devkit is installed]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include - ]], - [srtp_init();])], - [AC_MSG_RESULT(yes: version 2.x) - ac_external_srtp="2" - ac_external_srtp_lib="srtp2" - ], - [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include - ]], - [srtp_init();])], - [AC_MSG_RESULT(yes: version 1.x) - ac_external_srtp="1" - ac_external_srtp_lib="srtp" - ], - [AC_MSG_ERROR([Unable to use SRTP. If SRTP development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths])])]) - fi + [Use external SRTP development files, not the one in "third_party" directory. When this option is set, make sure that SRTP is accessible to use (hint: use CFLAGS and LDFLAGS env var to set the include/lib paths)]), + [ + if test "x$with_external_srtp" != "xno"; then + # Test SRTP installation + AC_MSG_CHECKING([if external SRTP devkit is installed]) + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include ]], + [srtp_init();]) + ], + [ + AC_MSG_RESULT(yes: version 2.x) + ac_external_srtp="2" + ac_external_srtp_lib="srtp2" + ], + [ + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include ]], + [srtp_init();]) + ], + [ + AC_MSG_RESULT(yes: version 1.x) + ac_external_srtp="1" + ac_external_srtp_lib="srtp" + ], + [ + AC_MSG_ERROR([Unable to use SRTP. If SRTP development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths]) + ] + ) + ] + ) + fi ] - ) +) dnl # For external SRTP, check availability of srtp_deinit() or srtp_shutdown() if test "x$ac_external_srtp" != "x0"; then - AC_SUBST(ac_srtp_deinit_present,0) - AC_SUBST(ac_srtp_shutdown_present,0) - AC_CHECK_LIB($ac_external_srtp_lib,srtp_deinit,[ac_srtp_deinit_present=1]) - if test "x$ac_srtp_deinit_present" != "x1"; then - AC_CHECK_LIB($ac_external_srtp_lib,srtp_shutdown,[ac_srtp_shutdown_present=1]) - fi + AC_SUBST(ac_srtp_deinit_present,0) + AC_SUBST(ac_srtp_shutdown_present,0) + AC_CHECK_LIB($ac_external_srtp_lib,srtp_deinit,[ac_srtp_deinit_present=1]) + if test "x$ac_srtp_deinit_present" != "x1"; then + AC_CHECK_LIB($ac_external_srtp_lib,srtp_shutdown,[ac_srtp_shutdown_present=1]) + fi fi - dnl # Use external libyuv installation AC_SUBST(ac_external_yuv,0) AC_ARG_WITH(external-yuv, AS_HELP_STRING([--with-external-yuv], - [Use external libyuv development files, not the one in "third_party" directory. When this option is set, make sure that libyuv is accessible to use (hint: use CFLAGS and LDFLAGS env var to set the include/lib paths)]), - [ - if test "x$with_external_yuv" != "xno"; then - # Test libyuv installation - AC_MSG_CHECKING([if external libyuv devkit is installed]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], - [RGB24ToI420(0,0,0,0,0,0,0,0,0,0);])], - [AC_MSG_RESULT(yes!!) - ac_external_yuv="1" - ], - [AC_MSG_ERROR([Unable to use external libyuv. If libyuv development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths])]) - fi + [Use external libyuv development files, not the one in "third_party" directory. When this option is set, make sure that libyuv is accessible to use (hint: use CFLAGS and LDFLAGS env var to set the include/lib paths)]), + [ + if test "x$with_external_yuv" != "xno"; then + # Test libyuv installation + AC_MSG_CHECKING([if external libyuv devkit is installed]) + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include ]], + [RGB24ToI420(0,0,0,0,0,0,0,0,0,0);]) + ], + [ + AC_MSG_RESULT(yes!!) + ac_external_yuv="1" + ], + [ + AC_MSG_ERROR([Unable to use external libyuv. If libyuv development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths]) + ] + ) + fi ] - ) +) dnl # Use external webrtc installation AC_SUBST(ac_external_webrtc,0) AC_ARG_WITH(external-webrtc, AS_HELP_STRING([--with-external-webrtc], - [Use external webrtc development files, not the one in "third_party" directory. When this option is set, make sure that webrtc is accessible to use (hint: use CFLAGS and LDFLAGS env var to set the include/lib paths)]), - [ - if test "x$with_external_webrtc" != "xno"; then - # Test webrtc installation - AC_MSG_CHECKING([if external webrtc devkit is installed]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include - #include - ]], - [WebRtcAec_Create();])], - [AC_MSG_RESULT(yes!!) - ac_external_webrtc="1" - ], - [AC_MSG_ERROR([Unable to use external webrtc. If webrtc development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths])]) - fi + [Use external webrtc development files, not the one in "third_party" directory. When this option is set, make sure that webrtc is accessible to use (hint: use CFLAGS and LDFLAGS env var to set the include/lib paths)]), + [ + if test "x$with_external_webrtc" != "xno"; then + # Test webrtc installation + AC_MSG_CHECKING([if external webrtc devkit is installed]) + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include + #include ]], + [WebRtcAec_Create();]) + ], + [ + AC_MSG_RESULT(yes!!) + ac_external_webrtc="1" + ], + [ + AC_MSG_ERROR([Unable to use external webrtc. If webrtc development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths]) + ] + ) + fi ] - ) +) dnl # Use external webrtc AEC3 installation AC_SUBST(ac_external_webrtc_aec3,0) AC_ARG_WITH(external-webrtc-aec3, AS_HELP_STRING([--with-external-webrtc-aec3], - [Use external webrtc AEC3 development files, not the one in "third_party" directory. When this option is set, make sure that webrtc is accessible to use (hint: use CFLAGS and LDFLAGS env var to set the include/lib paths)]), - [ - if test "x$with_external_webrtc_aec3" != "xno"; then - # Test webrtc AEC3 installation - AC_MSG_CHECKING([if external webrtc AEC3 is installed]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include "modules/audio_processing/aec3/echo_canceller3.h" - ]], - [EchoCanceller3 ec(EchoCanceller3Config(), 16000, 1, 1);])], - [AC_MSG_RESULT(yes!!) - ac_external_webrtc_aec3="1" - ], - [AC_MSG_ERROR([Unable to use external webrtc AEC3. If webrtc development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths])]) - fi + [Use external webrtc AEC3 development files, not the one in "third_party" directory. When this option is set, make sure that webrtc is accessible to use (hint: use CFLAGS and LDFLAGS env var to set the include/lib paths)]), + [ + if test "x$with_external_webrtc_aec3" != "xno"; then + # Test webrtc AEC3 installation + AC_MSG_CHECKING([if external webrtc AEC3 is installed]) + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include "modules/audio_processing/aec3/echo_canceller3.h"]], + [EchoCanceller3 ec(EchoCanceller3Config(), 16000, 1, 1);]) + ], + [ + AC_MSG_RESULT(yes!!) + ac_external_webrtc_aec3="1" + ], + [ + AC_MSG_ERROR([Unable to use external webrtc AEC3. If webrtc development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths]) + ] + ) + fi ] - ) +) dnl # Resample implementation AC_SUBST(ac_pjmedia_resample,libresample) AC_ARG_ENABLE(resample, - AS_HELP_STRING([--disable-resample], - [Disable resampling implementations]), - [if test "$enable_resample" = "no"; then - [ac_pjmedia_resample=none] - AC_MSG_RESULT([Checking if resampling is disabled...yes]) - fi] - ) + AS_HELP_STRING([--disable-resample], [Disable resampling implementations]), + [ + if test "$enable_resample" = "no"; then + [ac_pjmedia_resample=none] + AC_MSG_RESULT([Checking if resampling is disabled...yes]) + fi + ] +) dnl # Sound device backend selection AC_SUBST(ac_pjmedia_snd) AC_ARG_ENABLE(sound, - AS_HELP_STRING([--disable-sound], - [Exclude sound (i.e. use null sound)]), - [if test "$enable_sound" = "no"; then - [ac_pjmedia_snd=null] - AC_MSG_RESULT([Checking if sound is disabled... yes]) - fi] - ) + AS_HELP_STRING([--disable-sound], [Exclude sound (i.e. use null sound)]), + [ + if test "$enable_sound" = "no"; then + [ac_pjmedia_snd=null] + AC_MSG_RESULT([Checking if sound is disabled... yes]) + fi + ] +) dnl # Use external PortAudio installation AC_SUBST(ac_external_pa,0) AC_ARG_WITH(external-pa, AS_HELP_STRING([--with-external-pa], - [Use external PortAudio development files. When this option is set, make sure that PortAudio is accessible to use (hint: use CFLAGS and LDFLAGS env var to set the include/lib paths)]), - [ - if test "x$with_external_pa" != "xno"; then - # Test PortAudio installation - AC_MSG_CHECKING([if external PortAudio devkit is installed]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include - ]], - [Pa_Initialize();])], - [AC_MSG_RESULT(yes!!) - ac_external_pa="1" - ], - [AC_MSG_ERROR([Unable to use PortAudio. If PortAudio development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths])]) - fi + [Use external PortAudio development files. When this option is set, make sure that PortAudio is accessible to use (hint: use CFLAGS and LDFLAGS env var to set the include/lib paths)]), + [ + if test "x$with_external_pa" != "xno"; then + # Test PortAudio installation + AC_MSG_CHECKING([if external PortAudio devkit is installed]) + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include ]], + [Pa_Initialize();]) + ], + [ + AC_MSG_RESULT(yes!!) + ac_external_pa="1" + ], + [ + AC_MSG_ERROR([Unable to use PortAudio. If PortAudio development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths]) + ] + ) + fi ] - ) +) AC_SUBST(ac_pa_cflags) -AC_CHECK_HEADER(sys/soundcard.h, - [ac_pa_cflags="$ac_pa_cflags -DHAVE_SYS_SOUNDCARD_H"]) -AC_CHECK_HEADER(linux/soundcard.h, - [ac_pa_cflags="$ac_pa_cflags -DHAVE_LINUX_SOUNDCARD_H"]) -AC_CHECK_HEADER(machine/soundcard.h, - [ac_pa_cflags="$ac_pa_cflags -DHAVE_MACHINE_SOUNDCARD_H"]) +AC_CHECK_HEADER(sys/soundcard.h, [ac_pa_cflags="$ac_pa_cflags -DHAVE_SYS_SOUNDCARD_H"]) +AC_CHECK_HEADER(linux/soundcard.h, [ac_pa_cflags="$ac_pa_cflags -DHAVE_LINUX_SOUNDCARD_H"]) +AC_CHECK_HEADER(machine/soundcard.h, [ac_pa_cflags="$ac_pa_cflags -DHAVE_MACHINE_SOUNDCARD_H"]) if test "x$ac_cv_c_bigendian" = "xyes"; then - ac_pa_cflags="$ac_pa_cflags -DPA_BIG_ENDIAN" + ac_pa_cflags="$ac_pa_cflags -DPA_BIG_ENDIAN" else - ac_pa_cflags="$ac_pa_cflags -DPA_LITTLE_ENDIAN" + ac_pa_cflags="$ac_pa_cflags -DPA_LITTLE_ENDIAN" fi - dnl # Enable Android Oboe audio device AC_SUBST(ac_oboe_cflags) AC_SUBST(ac_oboe_ldflags) AC_ARG_WITH(oboe, - AS_HELP_STRING([--with-oboe], - [Enable Android Oboe audio device backend.]), - [ - if test "x$with_oboe" != "xno" -a "x$with_oboe" != "x"; then - OBOE_PREFIX=$with_oboe - OBOE_CFLAGS="-I$OBOE_PREFIX/prefab/modules/oboe/include" - OBOE_LDFLAGS="-L$OBOE_PREFIX/prefab/modules/oboe/libs/android.$TARGET_ABI" - AC_MSG_RESULT([Using Oboe prefix... $with_oboe]) - else - OBOE_CFLAGS="" - OBOE_LDFLAGS="" - fi - - AC_MSG_CHECKING([Oboe usability]) - - OBOE_LIBS="-loboe -lOpenSLES -llog" - - SAVED_LIBS="$LIBS" - SAVED_LDFLAGS="$LDFLAGS" - SAVED_CXXFLAGS="$CXXFLAGS" - - LIBS="$OBOE_LIBS $LIBS" - LDFLAGS="$OBOE_LDFLAGS $LDFLAGS" - CXXFLAGS="$OBOE_CFLAGS $CXXFLAGS" - - AC_LANG_PUSH([C++]) - AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], - [oboe::AudioStreamBuilder sb; sb.setDeviceId(0);] - )], - [ ac_oboe_cflags="-DPJMEDIA_AUDIO_DEV_HAS_OBOE=1 $OBOE_CFLAGS" - ac_oboe_ldflags="$OBOE_LDFLAGS $OBOE_LIBS" - AC_MSG_RESULT(yes) - ], - [ - LIBS="$SAVED_LIBS" - LDFLAGS="$SAVED_LDFLAGS" - AC_MSG_RESULT(no) - ]) - AC_LANG_POP([C++]) - - # Put back original CXXFLAGS as app won't needed it. - CXXFLAGS="$SAVED_CFLAGS" + AS_HELP_STRING([--with-oboe], [Enable Android Oboe audio device backend.]), + [ + if test "x$with_oboe" != "xno" -a "x$with_oboe" != "x"; then + OBOE_PREFIX=$with_oboe + OBOE_CFLAGS="-I$OBOE_PREFIX/prefab/modules/oboe/include" + OBOE_LDFLAGS="-L$OBOE_PREFIX/prefab/modules/oboe/libs/android.$TARGET_ABI" + AC_MSG_RESULT([Using Oboe prefix... $with_oboe]) + else + OBOE_CFLAGS="" + OBOE_LDFLAGS="" + fi + + AC_MSG_CHECKING([Oboe usability]) + + OBOE_LIBS="-loboe -lOpenSLES -llog" + + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CXXFLAGS="$CXXFLAGS" + + LIBS="$OBOE_LIBS $LIBS" + LDFLAGS="$OBOE_LDFLAGS $LDFLAGS" + CXXFLAGS="$OBOE_CFLAGS $CXXFLAGS" + + AC_LANG_PUSH([C++]) + AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include ]], + [oboe::AudioStreamBuilder sb; sb.setDeviceId(0);]) + ], + [ + ac_oboe_cflags="-DPJMEDIA_AUDIO_DEV_HAS_OBOE=1 $OBOE_CFLAGS" + ac_oboe_ldflags="$OBOE_LDFLAGS $OBOE_LIBS" + AC_MSG_RESULT(yes) + ], + [ + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + AC_MSG_RESULT(no) + ] + ) + AC_LANG_POP([C++]) + + # Put back original CXXFLAGS as app won't needed it. + CXXFLAGS="$SAVED_CFLAGS" ] - ) +) if test "$enable_sound" = "no"; then - true; + true; else - AC_SUBST(ac_pjmedia_audiodev_objs) - case $target in - *android*) - if test "x$ac_oboe_cflags" != "x"; then - AC_MSG_RESULT([Checking sound device backend... Oboe]) - else - LIBS="$LIBS -lOpenSLES" - AC_MSG_RESULT([Checking sound device backend... Android]) - fi - ;; - *-apple-darwin_ios*) - LIBS="$LIBS -framework CoreAudio -framework CoreFoundation -framework AudioToolbox -framework CFNetwork -framework UIKit -framework AVFoundation" - ac_pjmedia_audiodev_objs="coreaudio_dev.o" - AC_MSG_RESULT([Checking sound device backend... AudioUnit]) - ;; - *darwin*) - LIBS="$LIBS -framework CoreAudio -framework CoreServices -framework AudioUnit -framework AudioToolbox" - ac_pjmedia_audiodev_objs="coreaudio_dev.o" - if test "`uname -r`" = "6.8"; then - #ac_pa_cflags="$ac_pa_cflags -DPA_OLD_CORE_AUDIO -DMOSX_USE_NON_ATOMIC_FLAG_BITS" - #AC_MSG_RESULT([Setting additional PortAudio CFLAGS.. -DPA_OLD_CORE_AUDIO -DMOSX_USE_NON_ATOMIC_FLAG_BITS]) - #ac_pjmedia_snd=pa_old_darwinos - AC_MSG_RESULT([Checking sound device backend... old coreaudio]) - else - ac_pjmedia_snd=coreaudio - AC_MSG_RESULT([Checking sound device backend... coreaudio]) - fi - ;; - *cygwin* | *mingw*) - ac_pjmedia_snd=win32 - AC_MSG_RESULT([Checking sound device backend... win32 sound]) - ;; - *rtems*) - ac_pjmedia_snd=null - AC_MSG_RESULT([Checking sound device backend... null sound]) - ;; - *) - if test "x$ac_external_pa" != "x1"; then - dnl # Check if ALSA is available - AC_CHECK_HEADER(alsa/version.h, - [LIBS="$LIBS -lasound" - ac_pjmedia_snd=alsa]) - if test "x$ac_pjmedia_snd" = "xalsa"; then - AC_MSG_RESULT([Checking sound device backend... alsa]) - else - ac_pjmedia_snd=null - AC_MSG_RESULT([Checking sound device backend... null sound]) - fi - fi - ;; - esac + AC_SUBST(ac_pjmedia_audiodev_objs) + case $target in + *android*) + if test "x$ac_oboe_cflags" != "x"; then + AC_MSG_RESULT([Checking sound device backend... Oboe]) + else + LIBS="$LIBS -lOpenSLES" + AC_MSG_RESULT([Checking sound device backend... Android]) + fi + ;; + *-apple-darwin_ios*) + LIBS="$LIBS -framework CoreAudio -framework CoreFoundation -framework AudioToolbox -framework CFNetwork -framework UIKit -framework AVFoundation" + ac_pjmedia_audiodev_objs="coreaudio_dev.o" + AC_MSG_RESULT([Checking sound device backend... AudioUnit]) + ;; + *darwin*) + LIBS="$LIBS -framework CoreAudio -framework CoreServices -framework AudioUnit -framework AudioToolbox" + ac_pjmedia_audiodev_objs="coreaudio_dev.o" + if test "`uname -r`" = "6.8"; then + #ac_pa_cflags="$ac_pa_cflags -DPA_OLD_CORE_AUDIO -DMOSX_USE_NON_ATOMIC_FLAG_BITS" + #AC_MSG_RESULT([Setting additional PortAudio CFLAGS.. -DPA_OLD_CORE_AUDIO -DMOSX_USE_NON_ATOMIC_FLAG_BITS]) + #ac_pjmedia_snd=pa_old_darwinos + AC_MSG_RESULT([Checking sound device backend... old coreaudio]) + else + ac_pjmedia_snd=coreaudio + AC_MSG_RESULT([Checking sound device backend... coreaudio]) + fi + ;; + *cygwin* | *mingw*) + ac_pjmedia_snd=win32 + AC_MSG_RESULT([Checking sound device backend... win32 sound]) + ;; + *rtems*) + ac_pjmedia_snd=null + AC_MSG_RESULT([Checking sound device backend... null sound]) + ;; + *) + if test "x$ac_external_pa" != "x1"; then + dnl # Check if ALSA is available + AC_CHECK_HEADER(alsa/version.h, + [LIBS="$LIBS -lasound" + ac_pjmedia_snd=alsa]) + if test "x$ac_pjmedia_snd" = "xalsa"; then + AC_MSG_RESULT([Checking sound device backend... alsa]) + else + ac_pjmedia_snd=null + AC_MSG_RESULT([Checking sound device backend... null sound]) + fi + fi + ;; + esac fi AC_SUBST(ac_pjmedia_video) # Disable video on mingw by default (but respect --enable-video=yes) case $target in - *mingw*) - if test ! "$enable_video" = "yes"; then - enable_video="no" - fi - ;; + *mingw*) + if test ! "$enable_video" = "yes"; then + enable_video="no" + fi + ;; esac dnl # --disable-video option AC_ARG_ENABLE(video, - AS_HELP_STRING([--disable-video], - [Disable video feature]), - [if test "$enable_video" = "no"; then - #AC_DEFINE(PJMEDIA_HAS_VIDEO,0) - AC_MSG_RESULT([Video is disabled]) - enable_sdl="no" - enable_ffmpeg="no" - enable_v4l2="no" - enable_openh264="no" - enable_libyuv="no" - enable_vpx="no" - fi], - []) + AS_HELP_STRING([--disable-video], [Disable video feature]), + [ + if test "$enable_video" = "no"; then + #AC_DEFINE(PJMEDIA_HAS_VIDEO,0) + AC_MSG_RESULT([Video is disabled]) + enable_sdl="no" + enable_ffmpeg="no" + enable_v4l2="no" + enable_openh264="no" + enable_libyuv="no" + enable_vpx="no" + fi + ], + [] +) case $target in - *android*) - LIBS="$LIBS -llog" - ;; - *-apple-darwin_ios*) - LIBS="$LIBS -framework UIKit" - ;; - *darwin*) - LIBS="$LIBS -framework Foundation -framework AppKit" - ;; + *android*) + LIBS="$LIBS -llog" + ;; + *-apple-darwin_ios*) + LIBS="$LIBS -framework UIKit" + ;; + *darwin*) + LIBS="$LIBS -framework Foundation -framework AppKit" + ;; esac if test "$enable_video" = "no"; then - true; + true; else - case $target in - *android*) - ac_pjmedia_video=android_os - AC_SUBST(ac_pjmedia_video_has_android) - AC_SUBST(ac_android_cflags) - SAVED_LIBS="$LIBS" - LIBS="-lGLESv2 -lEGL -landroid -lc" - AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [])],[ac_pjmedia_video_has_android=yes],[ac_pjmedia_video_has_android=no]) - LIBS="$SAVED_LIBS" - if test "$ac_pjmedia_video_has_android" = "yes"; then - ac_android_cflags="-DPJMEDIA_VIDEO_DEV_HAS_ANDROID_OPENGL=1" - LIBS="$LIBS -lGLESv2 -lEGL -landroid" - AC_MSG_RESULT([Checking if OpenGL ES 2 is available... yes]) - else - AC_MSG_RESULT([Checking if OpenGL ES 2 is available... no]) - fi - ac_android_cflags="$ac_android_cflags -DPJMEDIA_VIDEO_DEV_HAS_ANDROID=1" - ;; - *mingw*) - if test "$enable_video" = "yes"; then - ac_pjmedia_video=windows_os - AC_SUBST(ac_pjmedia_video_dev_has_dshow) - ac_pjmedia_video_dev_has_dshow=yes - AC_SUBST(ac_dshow_cflags) - ac_dshow_cflags="-DPJMEDIA_HAS_VIDEO=1 -DPJMEDIA_VIDEO_DEV_HAS_DSHOW=1" - AC_SUBST(ac_dshow_ldflags) - ac_dshow_ldflags=" -lstdc++ -lquartz -lole32 -loleaut32 -lrpcrt4 -lwinmm -luuid -lmingwex -lstrmiids " - LIBS="$LIBS -lstdc++ -lquartz -lole32 -loleaut32 -lrpcrt4 -lwinmm -luuid -lmingwex -lstrmiids " - fi - ;; - *darwin*) - ac_pjmedia_video=darwin_os - AC_SUBST(ac_pjmedia_video_has_darwin) - AC_SUBST(ac_pjmedia_video_has_metal) - AC_SUBST(ac_pjmedia_video_has_vtoolbox) - AC_SUBST(ac_pjmedia_video_has_ios_opengl) - AC_SUBST(ac_darwin_cflags) - SAVED_LIBS="$LIBS" - LIBS="-framework AVFoundation -framework CoreGraphics -framework QuartzCore -framework CoreVideo -framework CoreMedia" - AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [])], - [ac_pjmedia_video_has_darwin=yes], - [ac_pjmedia_video_has_darwin=no]) - LIBS="-framework VideoToolbox" - AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [])], - [ac_pjmedia_video_has_vtoolbox=yes], - [ac_pjmedia_video_has_vtoolbox=no]) - LIBS="-framework Metal" - AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [])], - [ac_pjmedia_video_has_metal=yes], - [ac_pjmedia_video_has_metal=no]) - LIBS="-framework OpenGLES" - AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [])], - [ac_pjmedia_video_has_ios_opengl=yes], - [ac_pjmedia_video_has_ios_opengl=no]) - LIBS="$SAVED_LIBS" - if test "$ac_pjmedia_video_has_darwin" = "yes"; then - ac_darwin_cflags="-DPJMEDIA_VIDEO_DEV_HAS_DARWIN=1" - LIBS="$LIBS -framework AVFoundation -framework CoreGraphics -framework QuartzCore -framework CoreVideo -framework CoreMedia" - AC_MSG_RESULT([Checking if AVFoundation framework is available... yes]) - else - AC_MSG_RESULT([Checking if AVFoundation framework is available... no]) - fi - if test "$ac_pjmedia_video_has_metal" = "yes"; then - ac_darwin_cflags+=" -DPJMEDIA_VIDEO_DEV_HAS_METAL=1" - LIBS="$LIBS -framework Metal -framework MetalKit" - AC_MSG_RESULT([Checking if Metal framework is available... yes]) - else - AC_MSG_RESULT([Checking if Metal framework is available... no]) - fi - if test "$ac_pjmedia_video_has_vtoolbox" = "yes"; then - #ac_darwin_cflags+=" -DPJMEDIA_HAS_VID_TOOLBOX_CODEC=1" - LIBS="$LIBS -framework VideoToolbox" - AC_MSG_RESULT([Checking if VideoToolbox framework is available... yes]) - else - AC_MSG_RESULT([Checking if VideoToolbox framework is available... no]) - fi - if test "$ac_pjmedia_video_has_ios_opengl" = "yes"; then - ac_darwin_cflags+=" -DPJMEDIA_VIDEO_DEV_HAS_IOS_OPENGL=1" - LIBS="$LIBS -framework OpenGLES" - AC_MSG_RESULT([Checking if OpenGLES framework is available... yes]) - else - AC_MSG_RESULT([Checking if OpenGLES framework is available... no]) - fi - if false; then - # QTKit is deprecated, see ticket #1931. - ac_pjmedia_video=mac_os - AC_SUBST(ac_pjmedia_video_has_qt) - AC_SUBST(ac_qt_cflags) - SAVED_LIBS="$LIBS" - LIBS="-framework QTKit" - AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [])],[ac_pjmedia_video_has_qt=yes],[ac_pjmedia_video_has_qt=no]) - LIBS="$SAVED_LIBS" - if test "$ac_pjmedia_video_has_qt" = "yes"; then - ac_qt_cflags="-DPJMEDIA_VIDEO_DEV_HAS_QT=1" - LIBS="$LIBS -framework QTKit -framework QuartzCore -framework OpenGL" - AC_MSG_RESULT([Checking if QTKit framework is available... yes]) - else - AC_MSG_RESULT([Checking if QTKit framework is available... no]) - fi - fi - ;; - esac + case $target in + *android*) + ac_pjmedia_video=android_os + AC_SUBST(ac_pjmedia_video_has_android) + AC_SUBST(ac_android_cflags) + SAVED_LIBS="$LIBS" + LIBS="-lGLESv2 -lEGL -landroid -lc" + AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM([[]], []) + ], + [ac_pjmedia_video_has_android=yes], + [ac_pjmedia_video_has_android=no] + ) + LIBS="$SAVED_LIBS" + if test "$ac_pjmedia_video_has_android" = "yes"; then + ac_android_cflags="-DPJMEDIA_VIDEO_DEV_HAS_ANDROID_OPENGL=1" + LIBS="$LIBS -lGLESv2 -lEGL -landroid" + AC_MSG_RESULT([Checking if OpenGL ES 2 is available... yes]) + else + AC_MSG_RESULT([Checking if OpenGL ES 2 is available... no]) + fi + ac_android_cflags="$ac_android_cflags -DPJMEDIA_VIDEO_DEV_HAS_ANDROID=1" + ;; + *mingw*) + if test "$enable_video" = "yes"; then + ac_pjmedia_video=windows_os + AC_SUBST(ac_pjmedia_video_dev_has_dshow) + ac_pjmedia_video_dev_has_dshow=yes + AC_SUBST(ac_dshow_cflags) + ac_dshow_cflags="-DPJMEDIA_HAS_VIDEO=1 -DPJMEDIA_VIDEO_DEV_HAS_DSHOW=1" + AC_SUBST(ac_dshow_ldflags) + ac_dshow_ldflags=" -lstdc++ -lquartz -lole32 -loleaut32 -lrpcrt4 -lwinmm -luuid -lmingwex -lstrmiids " + LIBS="$LIBS -lstdc++ -lquartz -lole32 -loleaut32 -lrpcrt4 -lwinmm -luuid -lmingwex -lstrmiids " + fi + ;; + *darwin*) + ac_pjmedia_video=darwin_os + AC_SUBST(ac_pjmedia_video_has_darwin) + AC_SUBST(ac_pjmedia_video_has_metal) + AC_SUBST(ac_pjmedia_video_has_vtoolbox) + AC_SUBST(ac_pjmedia_video_has_ios_opengl) + AC_SUBST(ac_darwin_cflags) + SAVED_LIBS="$LIBS" + + LIBS="-framework AVFoundation -framework CoreGraphics -framework QuartzCore -framework CoreVideo -framework CoreMedia" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[]], [])], + [ac_pjmedia_video_has_darwin=yes], + [ac_pjmedia_video_has_darwin=no] + ) + + LIBS="-framework VideoToolbox" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[]], [])], + [ac_pjmedia_video_has_vtoolbox=yes], + [ac_pjmedia_video_has_vtoolbox=no]) + + LIBS="-framework Metal" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[]], [])], + [ac_pjmedia_video_has_metal=yes], + [ac_pjmedia_video_has_metal=no]) + + LIBS="-framework OpenGLES" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[]], [])], + [ac_pjmedia_video_has_ios_opengl=yes], + [ac_pjmedia_video_has_ios_opengl=no]) + + LIBS="$SAVED_LIBS" + if test "$ac_pjmedia_video_has_darwin" = "yes"; then + ac_darwin_cflags="-DPJMEDIA_VIDEO_DEV_HAS_DARWIN=1" + LIBS="$LIBS -framework AVFoundation -framework CoreGraphics -framework QuartzCore -framework CoreVideo -framework CoreMedia" + AC_MSG_RESULT([Checking if AVFoundation framework is available... yes]) + else + AC_MSG_RESULT([Checking if AVFoundation framework is available... no]) + fi + + if test "$ac_pjmedia_video_has_metal" = "yes"; then + ac_darwin_cflags+=" -DPJMEDIA_VIDEO_DEV_HAS_METAL=1" + LIBS="$LIBS -framework Metal -framework MetalKit" + AC_MSG_RESULT([Checking if Metal framework is available... yes]) + else + AC_MSG_RESULT([Checking if Metal framework is available... no]) + fi + + if test "$ac_pjmedia_video_has_vtoolbox" = "yes"; then + #ac_darwin_cflags+=" -DPJMEDIA_HAS_VID_TOOLBOX_CODEC=1" + LIBS="$LIBS -framework VideoToolbox" + AC_MSG_RESULT([Checking if VideoToolbox framework is available... yes]) + else + AC_MSG_RESULT([Checking if VideoToolbox framework is available... no]) + fi + + if test "$ac_pjmedia_video_has_ios_opengl" = "yes"; then + ac_darwin_cflags+=" -DPJMEDIA_VIDEO_DEV_HAS_IOS_OPENGL=1" + LIBS="$LIBS -framework OpenGLES" + AC_MSG_RESULT([Checking if OpenGLES framework is available... yes]) + else + AC_MSG_RESULT([Checking if OpenGLES framework is available... no]) + fi + + if false; then + # QTKit is deprecated, see ticket #1931. + ac_pjmedia_video=mac_os + AC_SUBST(ac_pjmedia_video_has_qt) + AC_SUBST(ac_qt_cflags) + SAVED_LIBS="$LIBS" + LIBS="-framework QTKit" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [])],[ac_pjmedia_video_has_qt=yes],[ac_pjmedia_video_has_qt=no]) + LIBS="$SAVED_LIBS" + if test "$ac_pjmedia_video_has_qt" = "yes"; then + ac_qt_cflags="-DPJMEDIA_VIDEO_DEV_HAS_QT=1" + LIBS="$LIBS -framework QTKit -framework QuartzCore -framework OpenGL" + AC_MSG_RESULT([Checking if QTKit framework is available... yes]) + else + AC_MSG_RESULT([Checking if QTKit framework is available... no]) + fi + fi + ;; + esac fi AC_ARG_ENABLE(ext_sound, - AS_HELP_STRING([--enable-ext-sound], - [PJMEDIA will not provide any sound device backend]), - [if test "$enable_ext_sound" = "yes"; then - [ac_pjmedia_snd=external] - AC_MSG_RESULT([Checking if external sound is set... yes]) - fi] - ) + AS_HELP_STRING([--enable-ext-sound], [PJMEDIA will not provide any sound device backend]), + [ + if test "$enable_ext_sound" = "yes"; then + [ac_pjmedia_snd=external] + AC_MSG_RESULT([Checking if external sound is set... yes]) + fi + ] +) dnl # Include resampling small filter AC_SUBST(ac_no_small_filter) AC_ARG_ENABLE(small-filter, - AS_HELP_STRING([--disable-small-filter], - [Exclude small filter in resampling]), - [if test "$enable_small_filter" = "no"; then - [ac_no_small_filter='-DPJMEDIA_HAS_SMALL_FILTER=0'] - AC_MSG_RESULT([Checking if small filter is disabled... yes]) - fi], - AC_MSG_RESULT([Checking if small filter is disabled... no])) + AS_HELP_STRING([--disable-small-filter], [Exclude small filter in resampling]), + [ + if test "$enable_small_filter" = "no"; then + [ac_no_small_filter='-DPJMEDIA_HAS_SMALL_FILTER=0'] + AC_MSG_RESULT([Checking if small filter is disabled... yes]) + fi + ], + AC_MSG_RESULT([Checking if small filter is disabled... no]) +) dnl # Include resampling large filter AC_SUBST(ac_no_large_filter) AC_ARG_ENABLE(large-filter, - AS_HELP_STRING([--disable-large-filter], - [Exclude large filter in resampling]), - [if test "$enable_large_filter" = "no"; then - [ac_no_large_filter='-DPJMEDIA_HAS_LARGE_FILTER=0'] - AC_MSG_RESULT([Checking if large filter is disabled... yes]) - fi], - AC_MSG_RESULT([Checking if large filter is disabled... no])) + AS_HELP_STRING([--disable-large-filter],[Exclude large filter in resampling]), + [ + if test "$enable_large_filter" = "no"; then + [ac_no_large_filter='-DPJMEDIA_HAS_LARGE_FILTER=0'] + AC_MSG_RESULT([Checking if large filter is disabled... yes]) + fi + ], + AC_MSG_RESULT([Checking if large filter is disabled... no]) +) dnl # Include Speex AEC AC_SUBST(ac_no_speex_aec) AC_ARG_ENABLE(speex-aec, - AS_HELP_STRING([--disable-speex-aec], - [Exclude Speex Acoustic Echo Canceller/AEC]), - [if test "$enable_speex_aec" = "no"; then - [ac_no_speex_aec='-DPJMEDIA_HAS_SPEEX_AEC=0'] - AC_MSG_RESULT([Checking if Speex AEC is disabled...yes]) - fi], - AC_MSG_RESULT([Checking if Speex AEC is disabled...no])) + AS_HELP_STRING([--disable-speex-aec], [Exclude Speex Acoustic Echo Canceller/AEC]), + [ + if test "$enable_speex_aec" = "no"; then + [ac_no_speex_aec='-DPJMEDIA_HAS_SPEEX_AEC=0'] + AC_MSG_RESULT([Checking if Speex AEC is disabled...yes]) + fi + ], + AC_MSG_RESULT([Checking if Speex AEC is disabled...no]) +) dnl # Include G711 codec AC_SUBST(ac_no_g711_codec) AC_ARG_ENABLE(g711-codec, - AS_HELP_STRING([--disable-g711-codec], - [Exclude G.711 codecs from the build]), - [if test "$enable_g711_codec" = "no"; then - [ac_no_g711_codec=1] - AC_DEFINE(PJMEDIA_HAS_G711_CODEC,0) - AC_MSG_RESULT([Checking if G.711 codec is disabled...yes]) - fi], - AC_MSG_RESULT([Checking if G.711 codec is disabled...no])) + AS_HELP_STRING([--disable-g711-codec], [Exclude G.711 codecs from the build]), + [ + if test "$enable_g711_codec" = "no"; then + [ac_no_g711_codec=1] + AC_DEFINE(PJMEDIA_HAS_G711_CODEC,0) + AC_MSG_RESULT([Checking if G.711 codec is disabled...yes]) + fi + ], + AC_MSG_RESULT([Checking if G.711 codec is disabled...no]) +) dnl # Include L16 codec AC_SUBST(ac_no_l16_codec) AC_ARG_ENABLE(l16-codec, - AS_HELP_STRING([--disable-l16-codec], - [Exclude Linear/L16 codec family from the build]), - [if test "$enable_l16_codec" = "no"; then - [ac_no_l16_codec=1] - AC_DEFINE(PJMEDIA_HAS_L16_CODEC,0) - AC_MSG_RESULT([Checking if L16 codecs are disabled...yes]) - fi], - AC_MSG_RESULT([Checking if L16 codec is disabled...no])) - + AS_HELP_STRING([--disable-l16-codec], [Exclude Linear/L16 codec family from the build]), + [ + if test "$enable_l16_codec" = "no"; then + [ac_no_l16_codec=1] + AC_DEFINE(PJMEDIA_HAS_L16_CODEC,0) + AC_MSG_RESULT([Checking if L16 codecs are disabled...yes]) + fi + ], + AC_MSG_RESULT([Checking if L16 codec is disabled...no]) +) dnl # Include GSM codec AC_SUBST(ac_no_gsm_codec) AC_ARG_ENABLE(gsm-codec, - AS_HELP_STRING([--disable-gsm-codec], - [Exclude GSM codec in the build]), - [if test "$enable_gsm_codec" = "no"; then - [ac_no_gsm_codec=1] - AC_DEFINE(PJMEDIA_HAS_GSM_CODEC,0) - AC_MSG_RESULT([Checking if GSM codec is disabled...yes]) - fi], - AC_MSG_RESULT([Checking if GSM codec is disabled...no])) + AS_HELP_STRING([--disable-gsm-codec], [Exclude GSM codec in the build]), + [ + if test "$enable_gsm_codec" = "no"; then + [ac_no_gsm_codec=1] + AC_DEFINE(PJMEDIA_HAS_GSM_CODEC,0) + AC_MSG_RESULT([Checking if GSM codec is disabled...yes]) + fi + ], + AC_MSG_RESULT([Checking if GSM codec is disabled...no]) +) dnl # Include G.722 codec AC_SUBST(ac_no_g722_codec) AC_ARG_ENABLE(g722-codec, - AS_HELP_STRING([--disable-g722-codec], - [Exclude G.722 codec in the build]), - [if test "$enable_g722_codec" = "no"; then - [ac_no_g722_codec=1] - AC_DEFINE(PJMEDIA_HAS_G722_CODEC,0) - AC_MSG_RESULT([Checking if G.722 codec is disabled...yes]) - fi], - AC_MSG_RESULT([Checking if G.722 codec is disabled...no])) + AS_HELP_STRING([--disable-g722-codec], [Exclude G.722 codec in the build]), + [ + if test "$enable_g722_codec" = "no"; then + [ac_no_g722_codec=1] + AC_DEFINE(PJMEDIA_HAS_G722_CODEC,0) + AC_MSG_RESULT([Checking if G.722 codec is disabled...yes]) + fi + ], + AC_MSG_RESULT([Checking if G.722 codec is disabled...no]) +) dnl # Include G722.1 codec AC_SUBST(ac_no_g7221_codec) AC_ARG_ENABLE(g7221-codec, - AS_HELP_STRING([--disable-g7221-codec], - [Exclude G.7221 codec in the build]), - [if test "$enable_g7221_codec" = "no"; then - [ac_no_g7221_codec=1] - AC_DEFINE(PJMEDIA_HAS_G7221_CODEC,0) - AC_MSG_RESULT([Checking if G.722.1 codec is disabled...yes]) - fi], - AC_MSG_RESULT([Checking if G.722.1 codec is disabled...no])) + AS_HELP_STRING([--disable-g7221-codec], [Exclude G.7221 codec in the build]), + [ + if test "$enable_g7221_codec" = "no"; then + [ac_no_g7221_codec=1] + AC_DEFINE(PJMEDIA_HAS_G7221_CODEC,0) + AC_MSG_RESULT([Checking if G.722.1 codec is disabled...yes]) + fi + ], + AC_MSG_RESULT([Checking if G.722.1 codec is disabled...no]) +) dnl # Include Speex codec AC_SUBST(ac_no_speex_codec) AC_ARG_ENABLE(speex-codec, - AS_HELP_STRING([--disable-speex-codec], - [Exclude Speex codecs in the build]), - [if test "$enable_speex_codec" = "no"; then - [ac_no_speex_codec=1] - AC_DEFINE(PJMEDIA_HAS_SPEEX_CODEC,0) - AC_MSG_RESULT([Checking if Speex codec is disabled...yes]) - fi], - AC_MSG_RESULT([Checking if Speex codec is disabled...no])) + AS_HELP_STRING([--disable-speex-codec], [Exclude Speex codecs in the build]), + [ + if test "$enable_speex_codec" = "no"; then + [ac_no_speex_codec=1] + AC_DEFINE(PJMEDIA_HAS_SPEEX_CODEC,0) + AC_MSG_RESULT([Checking if Speex codec is disabled...yes]) + fi + ], + AC_MSG_RESULT([Checking if Speex codec is disabled...no]) +) dnl # Include iLBC codec AC_SUBST(ac_no_ilbc_codec) AC_ARG_ENABLE(ilbc-codec, - AS_HELP_STRING([--disable-ilbc-codec], - [Exclude iLBC codec in the build]), - [if test "$enable_ilbc_codec" = "no"; then - [ac_no_ilbc_codec=1] - AC_DEFINE(PJMEDIA_HAS_ILBC_CODEC,0) - AC_MSG_RESULT([Checking if iLBC codec is disabled...yes]) - fi], - AC_MSG_RESULT([Checking if iLBC codec is disabled...no])) + AS_HELP_STRING([--disable-ilbc-codec], [Exclude iLBC codec in the build]), + [ + if test "$enable_ilbc_codec" = "no"; then + [ac_no_ilbc_codec=1] + AC_DEFINE(PJMEDIA_HAS_ILBC_CODEC,0) + AC_MSG_RESULT([Checking if iLBC codec is disabled...yes]) + fi + ], + AC_MSG_RESULT([Checking if iLBC codec is disabled...no]) +) dnl # Include libsamplerate AC_ARG_ENABLE(libsamplerate, - AS_HELP_STRING([--enable-libsamplerate], - [Link with libsamplerate when available.]), - [ - if test "$enable_libsamplerate" = "yes"; then - AC_MSG_RESULT([Checking if libsamplerate is enabled...yes]) - AC_CHECK_LIB(samplerate,src_new) - [ac_pjmedia_resample=libsamplerate] - else - AC_MSG_RESULT([Checking if libsamplerate is enabled...no]) - fi - ], AC_MSG_RESULT([Checking if libsamplerate is enabled...no])) + AS_HELP_STRING([--enable-libsamplerate], [Link with libsamplerate when available.]), + [ + if test "$enable_libsamplerate" = "yes"; then + AC_MSG_RESULT([Checking if libsamplerate is enabled...yes]) + AC_CHECK_LIB(samplerate,src_new) + [ac_pjmedia_resample=libsamplerate] + else + AC_MSG_RESULT([Checking if libsamplerate is enabled...no]) + fi + ], + AC_MSG_RESULT([Checking if libsamplerate is enabled...no]) +) AC_SUBST(ac_resample_dll) AC_ARG_ENABLE(resample_dll, - AS_HELP_STRING([--enable-resample-dll], - [Build libresample as shared library]), - [if test "$enable_resample_dll" = "yes"; then - [ac_resample_dll=1] - AC_MSG_RESULT([Building libresample as shared library... yes]) - fi], - AC_MSG_RESULT([Building libresample as shared library... no]) - ) + AS_HELP_STRING([--enable-resample-dll], [Build libresample as shared library]), + [ + if test "$enable_resample_dll" = "yes"; then + [ac_resample_dll=1] + AC_MSG_RESULT([Building libresample as shared library... yes]) + fi + ], + AC_MSG_RESULT([Building libresample as shared library... no]) +) dnl # Include Speex resample AC_ARG_ENABLE(speex-resample, - AS_HELP_STRING([--enable-speex-resample], - [Enable Speex resample]), - [ - if test "$enable_speex_resample" = "yes"; then - AC_MSG_RESULT([Checking if Speex resample is enabled... yes]) - [ac_pjmedia_resample=speex] - else - AC_MSG_RESULT([Checking if Speex resample is enabled... no]) - fi - ], AC_MSG_RESULT([Checking if Speex resample is enabled... no])) + AS_HELP_STRING([--enable-speex-resample], [Enable Speex resample]), + [ + if test "$enable_speex_resample" = "yes"; then + AC_MSG_RESULT([Checking if Speex resample is enabled... yes]) + [ac_pjmedia_resample=speex] + else + AC_MSG_RESULT([Checking if Speex resample is enabled... no]) + fi + ], + AC_MSG_RESULT([Checking if Speex resample is enabled... no]) +) dnl # SDL alt prefix AC_ARG_WITH(sdl, - AS_HELP_STRING([--with-sdl=DIR], - [Specify alternate libSDL prefix]), + AS_HELP_STRING([--with-sdl=DIR], [Specify alternate libSDL prefix]), [], [with_sdl=no] - ) +) dnl # Do not use default SDL installation if we are cross-compiling if test "x$ac_cross_compile" != "x" -a "x$with_sdl" = "xno"; then - enable_sdl=no + nable_sdl=no fi dnl # SDL AC_ARG_ENABLE(sdl, - AS_HELP_STRING([--disable-sdl], - [Disable SDL (default: not disabled)]), - [ - if test "$enable_sdl" = "no"; then - AC_MSG_RESULT([Checking if SDL is disabled... yes]) - fi - ], - [ - if test "x$with_sdl" != "xno" -a "x$with_sdl" != "x"; then - AC_MSG_RESULT([Using SDL prefix... $with_sdl]) - AC_PATH_PROGS(SDL_CONFIG,sdl2-config sdl-config,,$with_sdl/bin) - else - AC_PATH_PROGS(SDL_CONFIG, sdl2-config sdl-config) - fi - - AC_MSG_CHECKING([SDL availability]) - if test "x$SDL_CONFIG" = "x"; then - AC_MSG_RESULT([not found]) - elif (sh -c "$SDL_CONFIG --version" | grep -e '^1\.3' -e '^2\.') then - AC_SUBST(ac_sdl_cflags) - AC_SUBST(ac_sdl_ldflags) - ac_sdl_cflags=`$SDL_CONFIG --cflags` - ac_sdl_cflags="-DPJMEDIA_VIDEO_DEV_HAS_SDL=1 $ac_sdl_cflags" - ac_sdl_ldflags=`$SDL_CONFIG --libs` - ac_sdl_ldflags=`echo "${ac_sdl_ldflags}" | sed -e 's/-mwindows//g'` - LIBS="$LIBS $ac_sdl_ldflags" - else - AC_MSG_RESULT([Unsupported SDL version]) - fi - ]) - + AS_HELP_STRING([--disable-sdl], [Disable SDL (default: not disabled)]), + [ + if test "$enable_sdl" = "no"; then + AC_MSG_RESULT([Checking if SDL is disabled... yes]) + fi + ], + [ + if test "x$with_sdl" != "xno" -a "x$with_sdl" != "x"; then + AC_MSG_RESULT([Using SDL prefix... $with_sdl]) + AC_PATH_PROGS(SDL_CONFIG,sdl2-config sdl-config,,$with_sdl/bin) + else + AC_PATH_PROGS(SDL_CONFIG, sdl2-config sdl-config) + fi + + AC_MSG_CHECKING([SDL availability]) + if test "x$SDL_CONFIG" = "x"; then + AC_MSG_RESULT([not found]) + elif (sh -c "$SDL_CONFIG --version" | grep -e '^1\.3' -e '^2\.') then + AC_SUBST(ac_sdl_cflags) + AC_SUBST(ac_sdl_ldflags) + ac_sdl_cflags=`$SDL_CONFIG --cflags` + ac_sdl_cflags="-DPJMEDIA_VIDEO_DEV_HAS_SDL=1 $ac_sdl_cflags" + ac_sdl_ldflags=`$SDL_CONFIG --libs` + ac_sdl_ldflags=`echo "${ac_sdl_ldflags}" | sed -e 's/-mwindows//g'` + LIBS="$LIBS $ac_sdl_ldflags" + else + AC_MSG_RESULT([Unsupported SDL version]) + fi + ] +) AC_ARG_WITH(ffmpeg, - AS_HELP_STRING([--with-ffmpeg=DIR], - [Specify alternate FFMPEG prefix]), + AS_HELP_STRING([--with-ffmpeg=DIR], [Specify alternate FFMPEG prefix]), [], [with_ffmpeg=no] - ) +) dnl # Do not use default ffmpeg installation if we are cross-compiling if test "x$ac_cross_compile" != "x" -a "x$with_ffmpeg" = "xno"; then @@ -1353,157 +1565,133 @@ fi dnl # FFMPEG stuffs AC_ARG_ENABLE(ffmpeg, - AS_HELP_STRING([--disable-ffmpeg], - [Disable ffmpeg (default: not disabled)]), - [ - AC_SUBST(ac_has_ffmpeg,0) - if test "$enable_ffmpeg" = "no"; then - AC_MSG_RESULT([Checking if ffmpeg is disabled... yes]) - fi - ], - [ - AC_SUBST(ac_ffmpeg_cflags) - AC_SUBST(ac_ffmpeg_ldflags) - - FFMPEG_PREFIX="" - AC_SUBST(SAVED_PKG_CONFIG_PATH) - SAVED_PKG_CONFIG_PATH=$PKG_CONFIG_PATH - if test "x$with_ffmpeg" != "xno" -a "x$with_ffmpeg" != "x"; then - FFMPEG_PREFIX=$with_ffmpeg - AC_MSG_RESULT([Using ffmpeg prefix... $FFMPEG_PREFIX]) - export PKG_CONFIG_PATH=$FFMPEG_PREFIX/lib/pkgconfig - fi - - AC_CHECK_PROGS(PKG_CONFIG,pkg-config "python pkgconfig.py",none) - - if test "$PKG_CONFIG" != "none"; then - AC_MSG_CHECKING([ffmpeg packages]) - av_pkg="" - if $PKG_CONFIG --exists libavdevice; then - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVDEVICE=1" - av_pkg="$av_pkg libavdevice" - fi - if $PKG_CONFIG --exists libavformat; then - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVFORMAT=1" - av_pkg="$av_pkg libavformat" - fi - if $PKG_CONFIG --exists libavcodec; then - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCODEC=1" - av_pkg="$av_pkg libavcodec" - fi - if $PKG_CONFIG --exists libswscale; then - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBSWSCALE=1" - av_pkg="$av_pkg libswscale" - fi - if $PKG_CONFIG --exists libavutil; then - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVUTIL=1" - av_pkg="$av_pkg libavutil" - fi - - if test "x$av_pkg" = "x"; then - AC_MSG_RESULT([none detected (check the prefix)! **]) - else - AC_MSG_RESULT([$av_pkg]) - fi - - ac_ffmpeg_cflags="$ac_ffmpeg_cflags `$PKG_CONFIG --cflags $av_pkg`" - ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags `$PKG_CONFIG --libs $av_pkg`" - - else - dnl # - dnl # Use hardcoded values to configure ffmpeg - dnl # - - AC_MSG_RESULT([*** Warning: neither pkg-config nor python is available, ffmpeg dependency cannot be calculated. If ffmpeg libraries are not detected, you need to specify the correct CFLAGS and LDFLAGS settings for ffmpeg prior to invoking configure ***]) - - LIBS="-L$FFMPEG_PREFIX/lib $LIBS" - LDFLAGS="-L$FFMPEG_PREFIX/lib $LDFLAGS" - CFLAGS="-I$FFMPEG_PREFIX/include $CFLAGS" - - AC_CHECK_LIB(avdevice, - avdevice_version, - [ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVDEVICE=1" - ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavdevice" - ] - ) - AC_CHECK_LIB(avutil, - av_malloc, - [ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVUTIL=1" - ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavutil" - ] - ) - AC_CHECK_LIB(avcodec, - avcodec_init, - [ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCODEC=1" - ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavcodec" - ], - [], - [-lavutil] - ) - AC_CHECK_LIB(avformat, - av_register_all, - [ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVFORMAT=1" - ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavformat" - ], - [], - [-lavcodec -lavutil] - ) - AC_CHECK_LIB(swscale, - sws_scale, - [ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBSWSCALE=1" - ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lswscale" - ], - [], - [-lavutil] - ) - AC_CHECK_LIB(avcore, - avcore_version, - [ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCORE=1" - ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavcore" - ] - ) - - fi - - AC_CHECK_TYPES(enum AVPixelFormat, - [], - [ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_USE_OLD_FFMPEG=1"], - [[#include ]] - ) - - LIBS="$LIBS $ac_ffmpeg_ldflags" - export PKG_CONFIG_PATH=$SAVED_PKG_CONFIG_PATH - ] - ) + AS_HELP_STRING([--disable-ffmpeg], [Disable ffmpeg (default: not disabled)]), + [ + AC_SUBST(ac_has_ffmpeg,0) + if test "$enable_ffmpeg" = "no"; then + AC_MSG_RESULT([Checking if ffmpeg is disabled... yes]) + fi + ], + [ + AC_SUBST(ac_ffmpeg_cflags) + AC_SUBST(ac_ffmpeg_ldflags) + + FFMPEG_PREFIX="" + AC_SUBST(SAVED_PKG_CONFIG_PATH) + SAVED_PKG_CONFIG_PATH=$PKG_CONFIG_PATH + if test "x$with_ffmpeg" != "xno" -a "x$with_ffmpeg" != "x"; then + FFMPEG_PREFIX=$with_ffmpeg + AC_MSG_RESULT([Using ffmpeg prefix... $FFMPEG_PREFIX]) + export PKG_CONFIG_PATH=$FFMPEG_PREFIX/lib/pkgconfig + fi + + AC_CHECK_PROGS(PKG_CONFIG,pkg-config "python pkgconfig.py",none) + + if test "$PKG_CONFIG" != "none"; then + AC_MSG_CHECKING([ffmpeg packages]) + av_pkg="" + if $PKG_CONFIG --exists libavdevice; then + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVDEVICE=1" + av_pkg="$av_pkg libavdevice" + fi + if $PKG_CONFIG --exists libavformat; then + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVFORMAT=1" + av_pkg="$av_pkg libavformat" + fi + if $PKG_CONFIG --exists libavcodec; then + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCODEC=1" + av_pkg="$av_pkg libavcodec" + fi + if $PKG_CONFIG --exists libswscale; then + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBSWSCALE=1" + av_pkg="$av_pkg libswscale" + fi + if $PKG_CONFIG --exists libavutil; then + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVUTIL=1" + av_pkg="$av_pkg libavutil" + fi + + if test "x$av_pkg" = "x"; then + AC_MSG_RESULT([none detected (check the prefix)! **]) + else + AC_MSG_RESULT([$av_pkg]) + fi + + ac_ffmpeg_cflags="$ac_ffmpeg_cflags `$PKG_CONFIG --cflags $av_pkg`" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags `$PKG_CONFIG --libs $av_pkg`" + + else + dnl # + dnl # Use hardcoded values to configure ffmpeg + dnl # + + AC_MSG_RESULT([*** Warning: neither pkg-config nor python is available, ffmpeg dependency cannot be calculated. If ffmpeg libraries are not detected, you need to specify the correct CFLAGS and LDFLAGS settings for ffmpeg prior to invoking configure ***]) + + LIBS="-L$FFMPEG_PREFIX/lib $LIBS" + LDFLAGS="-L$FFMPEG_PREFIX/lib $LDFLAGS" + CFLAGS="-I$FFMPEG_PREFIX/include $CFLAGS" + + AC_CHECK_LIB(avdevice, avdevice_version, [ + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVDEVICE=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavdevice"]) + + AC_CHECK_LIB(avutil, av_malloc, [ + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVUTIL=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavutil"]) + + AC_CHECK_LIB(avcodec, avcodec_init, [ + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCODEC=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavcodec"], + [], [-lavutil]) + + AC_CHECK_LIB(avformat, av_register_all, [ + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVFORMAT=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavformat"], + [], [-lavcodec -lavutil]) + + AC_CHECK_LIB(swscale, sws_scale, [ + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBSWSCALE=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lswscale"], + [], [-lavutil]) + + AC_CHECK_LIB(avcore, avcore_version, [ + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCORE=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavcore"]) + fi + + AC_CHECK_TYPES(enum AVPixelFormat,[],[ + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_USE_OLD_FFMPEG=1"], + [[#include ]]) + + LIBS="$LIBS $ac_ffmpeg_ldflags" + export PKG_CONFIG_PATH=$SAVED_PKG_CONFIG_PATH + ] +) dnl # Video for Linux 2 AC_ARG_ENABLE(v4l2, - AS_HELP_STRING([--disable-v4l2], - [Disable Video4Linux2 (default: not disabled)]), - [ - if test "$enable_v4l2" = "no"; then - AC_MSG_RESULT([Checking if V4L2 is disabled... yes]) - fi - ], - [ - AC_SUBST(ac_v4l2_cflags) - AC_SUBST(ac_v4l2_ldflags) - AC_CHECK_LIB(v4l2, - v4l2_open, - [ac_v4l2_cflags="-DPJMEDIA_VIDEO_DEV_HAS_V4L2=1" - ac_v4l2_ldflags="-lv4l2" - LIBS="$LIBS -lv4l2" - ] - ) - ]) + AS_HELP_STRING([--disable-v4l2], [Disable Video4Linux2 (default: not disabled)]), + [ + if test "$enable_v4l2" = "no"; then + AC_MSG_RESULT([Checking if V4L2 is disabled... yes]) + fi + ], + [ + AC_SUBST(ac_v4l2_cflags) + AC_SUBST(ac_v4l2_ldflags) + AC_CHECK_LIB(v4l2, v4l2_open, [ + ac_v4l2_cflags="-DPJMEDIA_VIDEO_DEV_HAS_V4L2=1" + ac_v4l2_ldflags="-lv4l2" + LIBS="$LIBS -lv4l2"]) + ] +) dnl # OpenH264 alt prefix AC_ARG_WITH(openh264, - AS_HELP_STRING([--with-openh264=DIR], - [Specify alternate OpenH264 prefix]), - [], - [with_openh264=no] - ) + AS_HELP_STRING([--with-openh264=DIR], [Specify alternate OpenH264 prefix]), + [], + [with_openh264=no] +) dnl # Do not use default OpenH264 installation if we are cross-compiling if test "x$ac_cross_compile" != "x" -a "x$with_openh264" = "xno"; then @@ -1514,61 +1702,63 @@ dnl # OpenH264 AC_SUBST(ac_openh264_cflags) AC_SUBST(ac_openh264_ldflags) AC_ARG_ENABLE(openh264, - AS_HELP_STRING([--disable-openh264], - [Disable OpenH264 (default: not disabled)]), - [ - if test "$enable_openh264" = "no"; then - AC_MSG_RESULT([Checking if OpenH264 is disabled... yes]) - fi - ], - [ - if test "x$with_openh264" != "xno" -a "x$with_openh264" != "x"; then - OPENH264_PREFIX=$with_openh264 - OPENH264_CFLAGS="-I$OPENH264_PREFIX/include" - OPENH264_LDFLAGS="-L$OPENH264_PREFIX/lib" - AC_MSG_RESULT([Using OpenH264 prefix... $with_openh264]) - else - OPENH264_CFLAGS="" - OPENH264_LDFLAGS="" - fi - - AC_MSG_CHECKING([OpenH264 usability]) - - OPENH264_LIBS="-lopenh264 -lstdc++" - - SAVED_LIBS="$LIBS" - SAVED_LDFLAGS="$LDFLAGS" - SAVED_CFLAGS="$CFLAGS" - - LIBS="$OPENH264_LIBS $LIBS" - LDFLAGS="$OPENH264_LDFLAGS $LDFLAGS" - CFLAGS="$OPENH264_CFLAGS $CFLAGS" - - AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include - #include - ]], - [WelsCreateSVCEncoder(0);] - )], - [ ac_openh264_cflags="-DPJMEDIA_HAS_OPENH264_CODEC=1 $OPENH264_CFLAGS" - ac_openh264_ldflags="$OPENH264_LDFLAGS $OPENH264_LIBS" - AC_MSG_RESULT(yes) - ], - [ - LIBS="$SAVED_LIBS" - LDFLAGS="$SAVED_LDFLAGS" - CFLAGS="$SAVED_CFLAGS" - AC_MSG_RESULT(no) - ]) - - ]) + AS_HELP_STRING([--disable-openh264], [Disable OpenH264 (default: not disabled)]), + [ + if test "$enable_openh264" = "no"; then + AC_MSG_RESULT([Checking if OpenH264 is disabled... yes]) + fi + ], + [ + if test "x$with_openh264" != "xno" -a "x$with_openh264" != "x"; then + OPENH264_PREFIX=$with_openh264 + OPENH264_CFLAGS="-I$OPENH264_PREFIX/include" + OPENH264_LDFLAGS="-L$OPENH264_PREFIX/lib" + AC_MSG_RESULT([Using OpenH264 prefix... $with_openh264]) + else + OPENH264_CFLAGS="" + OPENH264_LDFLAGS="" + fi + + AC_MSG_CHECKING([OpenH264 usability]) + + OPENH264_LIBS="-lopenh264 -lstdc++" + + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CFLAGS="$CFLAGS" + + LIBS="$OPENH264_LIBS $LIBS" + LDFLAGS="$OPENH264_LDFLAGS $LDFLAGS" + CFLAGS="$OPENH264_CFLAGS $CFLAGS" + + AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include + #include ]], + [WelsCreateSVCEncoder(0);]) + ], + [ + ac_openh264_cflags="-DPJMEDIA_HAS_OPENH264_CODEC=1 $OPENH264_CFLAGS" + ac_openh264_ldflags="$OPENH264_LDFLAGS $OPENH264_LIBS" + AC_MSG_RESULT(yes) + ], + [ + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CFLAGS="$SAVED_CFLAGS" + AC_MSG_RESULT(no) + ] + ) + ] +) dnl # VPX alt prefix AC_ARG_WITH(vpx, - AS_HELP_STRING([--with-vpx=DIR], - [Specify alternate VPX prefix]), - [], - [with_vpx=no] - ) + AS_HELP_STRING([--with-vpx=DIR], [Specify alternate VPX prefix]), + [], + [with_vpx=no] +) dnl # Do not use default VPX installation if we are cross-compiling if test "x$ac_cross_compile" != "x" -a "x$with_vpx" = "xno"; then @@ -1579,276 +1769,282 @@ dnl # VPX AC_SUBST(ac_vpx_cflags) AC_SUBST(ac_vpx_ldflags) AC_ARG_ENABLE(vpx, - AS_HELP_STRING([--disable-vpx], - [Disable VPX (default: not disabled)]), - [ - if test "$enable_vpx" = "no"; then - AC_MSG_RESULT([Checking if VPX is disabled... yes]) - fi - ], - [ - if test "x$with_vpx" != "xno" -a "x$with_vpx" != "x"; then - VPX_PREFIX=$with_vpx - VPX_CFLAGS="-I$VPX_PREFIX/include" - VPX_LDFLAGS="-L$VPX_PREFIX/lib" - AC_MSG_RESULT([Using VPX prefix... $with_vpx]) - else - VPX_CFLAGS="" - VPX_LDFLAGS="" - fi - - AC_MSG_CHECKING([VPX usability]) - - VPX_LIBS="-lvpx" - - SAVED_LIBS="$LIBS" - SAVED_LDFLAGS="$LDFLAGS" - SAVED_CFLAGS="$CFLAGS" - - LIBS="$VPX_LIBS $LIBS" - LDFLAGS="$VPX_LDFLAGS $LDFLAGS" - CFLAGS="$VPX_CFLAGS $CFLAGS" - - AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include - #include - ]], - [vpx_codec_iface_t *(*enc_if)() = &vpx_codec_vp8_cx;] - )], - [ ac_vpx_cflags="-DPJMEDIA_HAS_VPX_CODEC=1 $VPX_CFLAGS" - ac_vpx_ldflags="$VPX_LDFLAGS $VPX_LIBS" - AC_MSG_RESULT(yes) - ], - [ - LIBS="$SAVED_LIBS" - LDFLAGS="$SAVED_LDFLAGS" - CFLAGS="$SAVED_CFLAGS" - AC_MSG_RESULT(no) - ]) - - ]) - + AS_HELP_STRING([--disable-vpx], [Disable VPX (default: not disabled)]), + [ + if test "$enable_vpx" = "no"; then + AC_MSG_RESULT([Checking if VPX is disabled... yes]) + fi + ], + [ + if test "x$with_vpx" != "xno" -a "x$with_vpx" != "x"; then + VPX_PREFIX=$with_vpx + VPX_CFLAGS="-I$VPX_PREFIX/include" + VPX_LDFLAGS="-L$VPX_PREFIX/lib" + AC_MSG_RESULT([Using VPX prefix... $with_vpx]) + else + VPX_CFLAGS="" + VPX_LDFLAGS="" + fi + + AC_MSG_CHECKING([VPX usability]) + + VPX_LIBS="-lvpx" + + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CFLAGS="$CFLAGS" + + LIBS="$VPX_LIBS $LIBS" + LDFLAGS="$VPX_LDFLAGS $LDFLAGS" + CFLAGS="$VPX_CFLAGS $CFLAGS" + + AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include + #include ]], + [vpx_codec_iface_t *(*enc_if)() = &vpx_codec_vp8_cx;]) + ], + [ + ac_vpx_cflags="-DPJMEDIA_HAS_VPX_CODEC=1 $VPX_CFLAGS" + ac_vpx_ldflags="$VPX_LDFLAGS $VPX_LIBS" + AC_MSG_RESULT(yes) + ], + [ + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CFLAGS="$SAVED_CFLAGS" + AC_MSG_RESULT(no) + ] + ) + ] +) dnl ######################################################## dnl # Intel IPP support dnl # AC_ARG_ENABLE(ipp, AS_HELP_STRING([--enable-ipp], - [Enable Intel IPP support. Specify the Intel IPP package and samples location using IPPROOT and IPPSAMPLES env var or with --with-ipp and --with-ipp-samples options]), + [Enable Intel IPP support. Specify the Intel IPP package and samples location using IPPROOT and IPPSAMPLES env var or with --with-ipp and --with-ipp-samples options]), [], [enable_ipp=no] - ) +) AC_ARG_WITH(ipp, - AS_HELP_STRING([--with-ipp=DIR], - [Specify the Intel IPP location]), + AS_HELP_STRING([--with-ipp=DIR], [Specify the Intel IPP location]), [], [with_ipp=no] - ) +) AC_ARG_WITH(ipp-samples, - AS_HELP_STRING([--with-ipp-samples=DIR], - [Specify the Intel IPP samples location]), + AS_HELP_STRING([--with-ipp-samples=DIR], [Specify the Intel IPP samples location]), [], [with_ipp_samples=no] - ) +) AC_ARG_WITH(ipp-arch, AS_HELP_STRING([--with-ipp-arch=ARCH], - [Specify the Intel IPP ARCH suffix, e.g. "64" or "em64t. Default is blank for IA32"]), + [Specify the Intel IPP ARCH suffix, e.g. "64" or "em64t. Default is blank for IA32"]), [], [with_ipp_arch=no] - ) +) if test "x$enable_ipp" != "xno"; then - dnl # + dnl # dnl # Verifying Intel IPP path dnl # AC_MSG_CHECKING([Intel IPP location]) if test "x$with_ipp" != "xno" -a "x$with_ipp" != "x"; then - AC_MSG_RESULT([$with_ipp]) - IPPROOT=$with_ipp + AC_MSG_RESULT([$with_ipp]) + IPPROOT=$with_ipp elif test "x$IPPROOT" = "x"; then - if test -d /opt/intel/ipp; then - IPPROOT=`ls -d /opt/intel/ipp/*/* | head -1` - AC_MSG_RESULT([autodetected in $IPPROOT]) - fi + if test -d /opt/intel/ipp; then + IPPROOT=`ls -d /opt/intel/ipp/*/* | head -1` + AC_MSG_RESULT([autodetected in $IPPROOT]) + fi else - AC_MSG_RESULT([$IPPROOT]) + AC_MSG_RESULT([$IPPROOT]) fi if test "x$with_ipp_arch" != "xno"; then - IPP_SUFFIX=$with_ipp_arch - AC_MSG_RESULT([IPP arch suffix is set to $IPP_SUFFIX]) + IPP_SUFFIX=$with_ipp_arch + AC_MSG_RESULT([IPP arch suffix is set to $IPP_SUFFIX]) else - IPP_SUFFIX="" - AC_MSG_RESULT([IPP arch suffix is set to empty]) + IPP_SUFFIX="" + AC_MSG_RESULT([IPP arch suffix is set to empty]) fi if test x$IPPROOT = x; then - AC_MSG_ERROR([the location is neither specified nor can be guessed. Please specify with IPPROOT env var or with --with-ipp option]) + AC_MSG_ERROR([the location is neither specified nor can be guessed. Please specify with IPPROOT env var or with --with-ipp option]) elif test ! -d $IPPROOT; then - AC_MSG_ERROR([not found]) + AC_MSG_ERROR([not found]) elif test ! -d $IPPROOT/include; then - AC_MSG_ERROR([directory doesn't seem to be valid]) + AC_MSG_ERROR([directory doesn't seem to be valid]) else - # IPP directory looks okay. - # Remove trailing backslash - IPPROOT=`echo $IPPROOT | sed 's/\/$//'` - - SAVED_CFLAGS="$CFLAGS" - SAVED_LDFLAGS="$LDFLAGS" - SAVED_LIBS="$LIBS" - - IPP_CFLAGS="-I$IPPROOT/include" - IPP_LIBS="-lippsc${IPP_SUFFIX} -lipps${IPP_SUFFIX} -lippcore${IPP_SUFFIX}" - -# - # Some differences between Mac OS X and Linux - case $target in - *darwin* ) - IPP_LDFLAGS="-L$IPPROOT/Libraries -L$IPPROOT/lib" - ;; - *) - # Find out where the libraries live. - IPP7_ARCH="" - if test -d $IPPROOT/lib/intel64; then - IPP7_ARCH="intel64" - elif test -d $IPPROOT/lib/ia32; then - IPP7_ARCH="ia32" - elif test -d $IPPROOT/lib/mic; then - IPP7_ARCH="mic" - fi - - if test -z "$IPP7_ARCH"; then - # IPP6 (and possibly below) - IPP_LDFLAGS="-L$IPPROOT/sharedlib" - IPP_LIBS="$IPP_LIBS -lippsr${IPP_SUFFIX} -lguide" - else - # IPP7 - if ! test -d $IPPROOT/../compiler; then - AC_MSG_ERROR([Cannot find $IPPROOT/../compiler directory. Please set IPPROOT variable correctly]) - fi - IPP_CFLAGS="$IPP_CFLAGS" - IPP_LDFLAGS="-L$IPPROOT/lib/intel64 -L$IPPROOT/../compiler/lib/$IPP7_ARCH" - IPP_LIBS="$IPP_LIBS -liomp5" - fi - ;; - esac - - #IPP_LDFLAGS="-L$IPPROOT/sharedlib" - #Static: - #IPP_LIBS="-lippscmerged -lippsrmerged -lippsmerged -lippcore" - - CFLAGS="$CFLAGS $IPP_CFLAGS" - LDFLAGS="$LDFLAGS $IPP_LDFLAGS" - LIBS="$IPP_LIBS $LIBS" - - - AC_MSG_CHECKING([Intel IPP usability]) - AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include - ]], - [ippStaticInit();])], - [AC_MSG_RESULT(yes)], - [AC_MSG_FAILURE([Error: unable to recognize your IPP installation. Make sure the paths and ARCH suffix are set correctly, run with --help for more info])]) - - CFLAGS="$SAVED_CFLAGS" - LDFLAGS="$SAVED_LDFLAGS" - LIBS="$SAVED_LIBS" + # IPP directory looks okay. + # Remove trailing backslash + IPPROOT=`echo $IPPROOT | sed 's/\/$//'` + + SAVED_CFLAGS="$CFLAGS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_LIBS="$LIBS" + + IPP_CFLAGS="-I$IPPROOT/include" + IPP_LIBS="-lippsc${IPP_SUFFIX} -lipps${IPP_SUFFIX} -lippcore${IPP_SUFFIX}" + + # Some differences between Mac OS X and Linux + case $target in + *darwin* ) + IPP_LDFLAGS="-L$IPPROOT/Libraries -L$IPPROOT/lib" + ;; + *) + # Find out where the libraries live. + IPP7_ARCH="" + if test -d $IPPROOT/lib/intel64; then + IPP7_ARCH="intel64" + elif test -d $IPPROOT/lib/ia32; then + IPP7_ARCH="ia32" + elif test -d $IPPROOT/lib/mic; then + IPP7_ARCH="mic" + fi + + if test -z "$IPP7_ARCH"; then + # IPP6 (and possibly below) + IPP_LDFLAGS="-L$IPPROOT/sharedlib" + IPP_LIBS="$IPP_LIBS -lippsr${IPP_SUFFIX} -lguide" + else + # IPP7 + if ! test -d $IPPROOT/../compiler; then + AC_MSG_ERROR([Cannot find $IPPROOT/../compiler directory. Please set IPPROOT variable correctly]) + fi + IPP_CFLAGS="$IPP_CFLAGS" + IPP_LDFLAGS="-L$IPPROOT/lib/intel64 -L$IPPROOT/../compiler/lib/$IPP7_ARCH" + IPP_LIBS="$IPP_LIBS -liomp5" + fi + ;; + esac + + #IPP_LDFLAGS="-L$IPPROOT/sharedlib" + #Static: + #IPP_LIBS="-lippscmerged -lippsrmerged -lippsmerged -lippcore" + + CFLAGS="$CFLAGS $IPP_CFLAGS" + LDFLAGS="$LDFLAGS $IPP_LDFLAGS" + LIBS="$IPP_LIBS $LIBS" + + AC_MSG_CHECKING([Intel IPP usability]) + AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include ]], + [ippStaticInit();]) + ], + [AC_MSG_RESULT(yes)], + [AC_MSG_FAILURE([Error: unable to recognize your IPP installation. Make sure the paths and ARCH suffix are set correctly, run with --help for more info])] + ) + + CFLAGS="$SAVED_CFLAGS" + LDFLAGS="$SAVED_LDFLAGS" + LIBS="$SAVED_LIBS" fi - dnl # + dnl # dnl # Verifying Intel IPP samples path dnl # AC_MSG_CHECKING([Intel IPP samples location]) if test "x$with_ipp_samples" != "xno" -a "x$with_ipp_samples" != "x"; then - AC_MSG_RESULT([$with_ipp_samples]) - IPPSAMPLES=$with_ipp_samples + AC_MSG_RESULT([$with_ipp_samples]) + IPPSAMPLES=$with_ipp_samples elif test "x$IPPSAMPLES" = "x"; then - if test -d /opt/intel/ipp-samples; then - IPPSAMPLES=/opt/intel/ipp-samples - AC_MSG_RESULT([autodetected in $IPPSAMPLES]) - fi + if test -d /opt/intel/ipp-samples; then + IPPSAMPLES=/opt/intel/ipp-samples + AC_MSG_RESULT([autodetected in $IPPSAMPLES]) + fi else - AC_MSG_RESULT([$IPPSAMPLES]) + AC_MSG_RESULT([$IPPSAMPLES]) fi if test x$IPPSAMPLES = x; then - AC_MSG_ERROR([the location is neither specified nor can be guessed. Please specify with IPPSAMPLES env var or with --with-ipp-samples option]) + AC_MSG_ERROR([the location is neither specified nor can be guessed. Please specify with IPPSAMPLES env var or with --with-ipp-samples option]) elif test ! -d $IPPSAMPLES; then - AC_MSG_ERROR([not found]) + AC_MSG_ERROR([not found]) elif test ! -d $IPPSAMPLES/speech-codecs; then - AC_MSG_ERROR([directory doesn't seem to be valid]) + AC_MSG_ERROR([directory doesn't seem to be valid]) else - # Remove trailing backslash - IPPSAMPLES=`echo $IPPSAMPLES | sed 's/\/$//'` - - # Guess the libusc.a/libspeech.a build location - AC_MSG_CHECKING([Intel IPP USC build location]) - if test -d $IPPSAMPLES/speech-codecs/bin; then - IPPVER=5 - IPPSAMP_DIR=`ls -d $IPPSAMPLES/speech-codecs/bin/*gcc*/lib | head -1` - elif test -d $IPPSAMPLES/speech-codecs/_bin; then - IPPVER=6 - if test -d $IPPSAMPLES/speech-codecs/_bin/*gcc*; then - # gcc compiler - IPPSAMP_DIR=`ls -d $IPPSAMPLES/speech-codecs/_bin/*gcc*/lib | head -1` - elif test -d $IPPSAMPLES/speech-codecs/_bin/*icc*; then - # icc compiler - IPPSAMP_DIR=`ls -d $IPPSAMPLES/speech-codecs/_bin/*icc*/lib | head -1` - else - AC_MSG_FAILURE([Unable to find to find built binaries under $IPPSAMPLES/speech-codecs/{bin,_bin}. Have you built the IPP samples?]) - fi - else - AC_MSG_FAILURE([unable to find $IPPSAMPLES/speech-codecs/bin/*gcc*/lib or $IPPSAMPLES/speech-codecs/_bin/*gcc*/lib directory. Have you built the samples?]) - fi - - # Test the directory - if test ! -d $IPPSAMP_DIR; then - AC_MSG_FAILURE([There's something wrong with this script, directory $IPPSAMP_DIR does not exist]) - exit 1; - fi - - if test "x$IPPVER" = "x5"; then - IPPSAMP_LIBS="libusc.a" - IPPSAMP_LDLIBS="-lusc" - elif test "x$IPPVER" = "x6"; then - IPPSAMP_LIBS="libspeech.a" - IPPSAMP_LDLIBS="-lspeech" - else - AC_MSG_FAILURE([bug in this script: unsupported IPP version]) - fi - - if test ! -f $IPPSAMP_DIR/$IPPSAMP_LIBS; then - AC_MSG_FAILURE([$IPPSAMP_LIBS doesn't exist in $IPPSAMP_DIR]) - fi - - AC_MSG_RESULT([$IPPSAMP_DIR]) - - SAVED_CFLAGS="$CFLAGS" - SAVED_LDFLAGS="$LDFLAGS" - SAVED_LIBS="$LIBS" - - IPPSAMP_INC="-I$IPPSAMPLES/speech-codecs/core/usc/include" - CFLAGS="$CFLAGS $IPPSAMP_INC" - LDFLAGS="$LDFLAGS -L$IPPSAMP_DIR" - LIBS="$IPPSAMP_LDLIBS $LIBS" - - AC_MSG_CHECKING([Intel IPP USC usability]) - AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include - ]], - [extern USC_Fxns USC_G729AFP_Fxns;])], - [AC_MSG_RESULT(yes)], - [AC_MSG_FAILURE(no)]) - - CFLAGS="$SAVED_CFLAGS" - LDFLAGS="$IPP_LDFLAGS $SAVED_LDFLAGS" - LIBS="$IPP_LIBS $SAVED_LIBS" - - IPP_CFLAGS="$IPP_CFLAGS $IPPSAMP_INC" - IPP_LDFLAGS="$IPP_LDFLAGS -L$IPPSAMP_DIR" - IPP_LIBS="$IPPSAMP_LDLIBS $IPP_LIBS" + # Remove trailing backslash + IPPSAMPLES=`echo $IPPSAMPLES | sed 's/\/$//'` + + # Guess the libusc.a/libspeech.a build location + AC_MSG_CHECKING([Intel IPP USC build location]) + if test -d $IPPSAMPLES/speech-codecs/bin; then + IPPVER=5 + IPPSAMP_DIR=`ls -d $IPPSAMPLES/speech-codecs/bin/*gcc*/lib | head -1` + elif test -d $IPPSAMPLES/speech-codecs/_bin; then + IPPVER=6 + if test -d $IPPSAMPLES/speech-codecs/_bin/*gcc*; then + # gcc compiler + IPPSAMP_DIR=`ls -d $IPPSAMPLES/speech-codecs/_bin/*gcc*/lib | head -1` + elif test -d $IPPSAMPLES/speech-codecs/_bin/*icc*; then + # icc compiler + IPPSAMP_DIR=`ls -d $IPPSAMPLES/speech-codecs/_bin/*icc*/lib | head -1` + else + AC_MSG_FAILURE([Unable to find to find built binaries under $IPPSAMPLES/speech-codecs/{bin,_bin}. Have you built the IPP samples?]) + fi + else + AC_MSG_FAILURE([unable to find $IPPSAMPLES/speech-codecs/bin/*gcc*/lib or $IPPSAMPLES/speech-codecs/_bin/*gcc*/lib directory. Have you built the samples?]) + fi + + # Test the directory + if test ! -d $IPPSAMP_DIR; then + AC_MSG_FAILURE([There's something wrong with this script, directory $IPPSAMP_DIR does not exist]) + exit 1; + fi + + if test "x$IPPVER" = "x5"; then + IPPSAMP_LIBS="libusc.a" + IPPSAMP_LDLIBS="-lusc" + elif test "x$IPPVER" = "x6"; then + IPPSAMP_LIBS="libspeech.a" + IPPSAMP_LDLIBS="-lspeech" + else + AC_MSG_FAILURE([bug in this script: unsupported IPP version]) + fi + + if test ! -f $IPPSAMP_DIR/$IPPSAMP_LIBS; then + AC_MSG_FAILURE([$IPPSAMP_LIBS doesn't exist in $IPPSAMP_DIR]) + fi + + AC_MSG_RESULT([$IPPSAMP_DIR]) + + SAVED_CFLAGS="$CFLAGS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_LIBS="$LIBS" + + IPPSAMP_INC="-I$IPPSAMPLES/speech-codecs/core/usc/include" + CFLAGS="$CFLAGS $IPPSAMP_INC" + LDFLAGS="$LDFLAGS -L$IPPSAMP_DIR" + LIBS="$IPPSAMP_LDLIBS $LIBS" + + AC_MSG_CHECKING([Intel IPP USC usability]) + AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include ]], + [extern USC_Fxns USC_G729AFP_Fxns;]) + ], + [AC_MSG_RESULT(yes)], + [AC_MSG_FAILURE(no)] + ) + + CFLAGS="$SAVED_CFLAGS" + LDFLAGS="$IPP_LDFLAGS $SAVED_LDFLAGS" + LIBS="$IPP_LIBS $SAVED_LIBS" + + IPP_CFLAGS="$IPP_CFLAGS $IPPSAMP_INC" + IPP_LDFLAGS="$IPP_LDFLAGS -L$IPPSAMP_DIR" + IPP_LIBS="$IPPSAMP_LDLIBS $IPP_LIBS" fi CFLAGS="$CFLAGS $IPP_CFLAGS" @@ -1856,9 +2052,9 @@ if test "x$enable_ipp" != "xno"; then LIBS="$LIBS $IPP_LIBS" ac_build_mak_vars="$ac_build_mak_vars\n\ -export IPP_CFLAGS=$IPP_CFLAGS\n\ -export IPP_LDFLAGS=$IPP_LDFLAGS\n\ -export IPP_LIBS=$IPP_LIBS" + export IPP_CFLAGS=$IPP_CFLAGS\n\ + export IPP_LDFLAGS=$IPP_LDFLAGS\n\ + export IPP_LIBS=$IPP_LIBS" else AC_MSG_RESULT([Skipping Intel IPP settings (not wanted)]) fi @@ -1867,25 +2063,29 @@ dnl # Include Android MediaCodec AC_SUBST(ac_no_mediacodec) AC_ARG_ENABLE(android-mediacodec, - AS_HELP_STRING([--disable-android-mediacodec], - [Exclude Android MediaCodec (default: autodetect)]), - [if test "$enable_android_mediacodec" = "no"; then - [ac_no_mediacodec=1] - AC_MSG_RESULT([Checking if Android MediaCodec support is disabled... yes]) - fi], - [ - case $target in - *android*) - AC_CHECK_LIB(mediandk,AMediaCodec_createDecoderByType,[ac_pjmedia_has_amediacodec=1 && LIBS="-lmediandk $LIBS"]) - if test "x$ac_pjmedia_has_amediacodec" = "x1"; then - AC_MSG_RESULT([Checking if Android AMediaCodec library is available... yes]) - AC_DEFINE(PJMEDIA_HAS_ANDROID_MEDIACODEC, 1) - else - AC_MSG_RESULT([Checking if Android AMediaCodec library is available... no]) - fi - ;; - esac - ]) + AS_HELP_STRING([--disable-android-mediacodec], [Exclude Android MediaCodec (default: autodetect)]), + [ + if test "$enable_android_mediacodec" = "no"; then + [ac_no_mediacodec=1] + AC_MSG_RESULT([Checking if Android MediaCodec support is disabled... yes]) + fi + ], + [ + case $target in + *android*) + AC_CHECK_LIB(mediandk, AMediaCodec_createDecoderByType, [ + ac_pjmedia_has_amediacodec=1 && LIBS="-lmediandk $LIBS"]) + + if test "x$ac_pjmedia_has_amediacodec" = "x1"; then + AC_MSG_RESULT([Checking if Android AMediaCodec library is available... yes]) + AC_DEFINE(PJMEDIA_HAS_ANDROID_MEDIACODEC, 1) + else + AC_MSG_RESULT([Checking if Android AMediaCodec library is available... no]) + fi + ;; + esac + ] +) dnl ########################################## dnl # @@ -1895,20 +2095,19 @@ dnl # dnl # SSL alt prefix AC_ARG_WITH(ssl, AS_HELP_STRING([--with-ssl=DIR], - [Specify alternate SSL library prefix. This option will try - to find OpenSSL first, then if not found, GnuTLS. To skip - OpenSSL finding, use --with-gnutls option instead.]), + [Specify alternate SSL library prefix. This option will try + to find OpenSSL first, then if not found, GnuTLS. To skip + OpenSSL finding, use --with-gnutls option instead.]), [], [with_ssl=no] - ) +) dnl # GnuTLS alt prefix AC_ARG_WITH(gnutls, - AS_HELP_STRING([--with-gnutls=DIR], - [Specify alternate GnuTLS prefix]), + AS_HELP_STRING([--with-gnutls=DIR], [Specify alternate GnuTLS prefix]), [], [with_gnutls=no] - ) +) dnl # Do not use default SSL installation if we are cross-compiling if test "x$ac_cross_compile" != "x" -a "x$with_ssl" = "xno" -a "x$with_gnutls" = "xno"; then @@ -1921,154 +2120,174 @@ AC_SUBST(ac_ssl_has_aes_gcm,0) AC_SUBST(ac_ssl_backend) AC_ARG_ENABLE(darwin-ssl, - AS_HELP_STRING([--disable-darwin-ssl], - [Exclude Darwin SSL (default: autodetect)]), - [if test "$enable_darwin_ssl" = "no"; then - AC_MSG_RESULT([Checking if Darwin SSL support is disabled... yes]) - fi], - [ - case $target in - *darwin*) - SAVED_CFLAGS="$CFLAGS" - CFLAGS="-Werror" - SAVED_LIBS="$LIBS" - LIBS="-framework Security" - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], - [if (__builtin_available(macOS 10.12, iOS 10.0, *)) { - SSLContextRef ssl_ctx; - SSLReHandshake(ssl_ctx); - }])], - [ac_ssl_backend=darwin],) - CFLAGS="$SAVED_CFLAGS" - LIBS="$SAVED_LIBS" - if test "x$ac_ssl_backend" = "xdarwin"; then - AC_DEFINE(PJ_HAS_SSL_SOCK, 1) - AC_DEFINE(PJ_SSL_SOCK_IMP, PJ_SSL_SOCK_IMP_DARWIN) - LIBS="$LIBS -framework Security" - AC_MSG_RESULT([Checking if Darwin SSL is available... yes]) - else - AC_MSG_RESULT([Checking if Darwin SSL is available... no]) - fi - ;; - esac - ]) + AS_HELP_STRING([--disable-darwin-ssl], [Exclude Darwin SSL (default: autodetect)]), + [ + if test "$enable_darwin_ssl" = "no"; then + AC_MSG_RESULT([Checking if Darwin SSL support is disabled... yes]) + fi + ], + [ + case $target in + *darwin*) + SAVED_CFLAGS="$CFLAGS" + CFLAGS="-Werror" + SAVED_LIBS="$LIBS" + + LIBS="-framework Security" + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include ]], + [if (__builtin_available(macOS 10.12, iOS 10.0, *)) { + SSLContextRef ssl_ctx; + SSLReHandshake(ssl_ctx); + }]) + ], + [ac_ssl_backend=darwin], + ) + + CFLAGS="$SAVED_CFLAGS" + LIBS="$SAVED_LIBS" + if test "x$ac_ssl_backend" = "xdarwin"; then + AC_DEFINE(PJ_HAS_SSL_SOCK, 1) + AC_DEFINE(PJ_SSL_SOCK_IMP, PJ_SSL_SOCK_IMP_DARWIN) + LIBS="$LIBS -framework Security" + AC_MSG_RESULT([Checking if Darwin SSL is available... yes]) + else + AC_MSG_RESULT([Checking if Darwin SSL is available... no]) + fi + ;; + esac + ] +) AC_ARG_ENABLE(ssl, - AS_HELP_STRING([--disable-ssl], - [Exclude SSL support the build (default: autodetect)]) - , - [ - if test "$enable_ssl" = "no"; then - [ac_no_ssl=1] - AC_MSG_RESULT([Checking if SSL support is disabled... yes]) - fi - ], - [ - if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then - CFLAGS="$CFLAGS -I$with_ssl/include" - CPPFLAGS="$CPPFLAGS -I$with_ssl/include" - LDFLAGS="$LDFLAGS -L$with_ssl/lib" - AC_MSG_RESULT([Using SSL prefix... $with_ssl]) + AS_HELP_STRING([--disable-ssl], [Exclude SSL support the build (default: autodetect)]), + [ + if test "$enable_ssl" = "no"; then + [ac_no_ssl=1] + AC_MSG_RESULT([Checking if SSL support is disabled... yes]) + fi + ], + [ + if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then + CFLAGS="$CFLAGS -I$with_ssl/include" + CPPFLAGS="$CPPFLAGS -I$with_ssl/include" + LDFLAGS="$LDFLAGS -L$with_ssl/lib" + AC_MSG_RESULT([Using SSL prefix... $with_ssl]) + fi + + if test "x$with_gnutls" = "xno"; then + # We still need to check for OpenSSL installations even if + # we find Darwin SSL above since DTLS requires OpenSSL. + AC_MSG_RESULT([checking for OpenSSL installations..]) + AC_SUBST(openssl_h_present) + AC_SUBST(libssl_present) + AC_SUBST(libcrypto_present) + AC_CHECK_HEADER(openssl/ssl.h,[openssl_h_present=1]) + + AC_CHECK_LIB(crypto,ERR_load_BIO_strings,[ + libcrypto_present=1 && LIBS="-lcrypto $LIBS"], + [ + AC_CHECK_LIB(crypto,ERR_get_error,[libcrypto_present=1 && LIBS="-lcrypto $LIBS"]) + ] + ) + + AC_CHECK_LIB(ssl,SSL_CTX_new,[libssl_present=1 && LIBS="-lssl $LIBS"]) + + if test "x$openssl_h_present" = "x1" -a "x$libssl_present" = "x1" -a "x$libcrypto_present" = "x1"; then + AC_MSG_RESULT([OpenSSL library found, SSL support enabled]) + + # Check if SRTP should be compiled with OpenSSL + # support, to enable cryptos such as AES GCM. + + # EVP_CIPHER_CTX is now opaque in OpenSSL 1.1.0, libsrtp 1.5.4 uses it as a transparent type. + # Update 2.7: our bundled libsrtp has been upgraded to 2.1.0, + # so we can omit EVP_CIPHER_CTX definition check now. + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include ]], + [EVP_CIPHER_CTX *ctx;EVP_aes_128_gcm();]) + ], + [ + AC_CHECK_LIB(crypto,EVP_aes_128_gcm,[ac_ssl_has_aes_gcm=1]) + ] + ) + + if test "x$ac_ssl_has_aes_gcm" = "x1"; then + AC_MSG_RESULT([OpenSSL has AES GCM support, SRTP will use OpenSSL]) + else + AC_MSG_RESULT([OpenSSL AES GCM support not found, SRTP will only support AES CM cryptos]) fi - if test "x$with_gnutls" = "xno"; then - # We still need to check for OpenSSL installations even if - # we find Darwin SSL above since DTLS requires OpenSSL. - AC_MSG_RESULT([checking for OpenSSL installations..]) - AC_SUBST(openssl_h_present) - AC_SUBST(libssl_present) - AC_SUBST(libcrypto_present) - AC_CHECK_HEADER(openssl/ssl.h,[openssl_h_present=1]) - AC_CHECK_LIB(crypto,ERR_load_BIO_strings,[libcrypto_present=1 && LIBS="-lcrypto $LIBS"], [AC_CHECK_LIB(crypto,ERR_get_error,[libcrypto_present=1 && LIBS="-lcrypto $LIBS"])]) - AC_CHECK_LIB(ssl,SSL_CTX_new,[libssl_present=1 && LIBS="-lssl $LIBS"]) - if test "x$openssl_h_present" = "x1" -a "x$libssl_present" = "x1" -a "x$libcrypto_present" = "x1"; then - AC_MSG_RESULT([OpenSSL library found, SSL support enabled]) - - # Check if SRTP should be compiled with OpenSSL - # support, to enable cryptos such as AES GCM. - - # EVP_CIPHER_CTX is now opaque in OpenSSL 1.1.0, libsrtp 1.5.4 uses it as a transparent type. - # Update 2.7: our bundled libsrtp has been upgraded to 2.1.0, - # so we can omit EVP_CIPHER_CTX definition check now. - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], - [EVP_CIPHER_CTX *ctx;EVP_aes_128_gcm();])], - [AC_CHECK_LIB(crypto,EVP_aes_128_gcm,[ac_ssl_has_aes_gcm=1])]) - if test "x$ac_ssl_has_aes_gcm" = "x1"; then - AC_MSG_RESULT([OpenSSL has AES GCM support, SRTP will use OpenSSL]) - else - AC_MSG_RESULT([OpenSSL AES GCM support not found, SRTP will only support AES CM cryptos]) - fi - - if test "x$ac_ssl_backend" = "x"; then - # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK - #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1) - AC_DEFINE(PJ_HAS_SSL_SOCK, 1) - AC_DEFINE(PJ_SSL_SOCK_IMP, PJ_SSL_SOCK_IMP_OPENSSL) - ac_ssl_backend="openssl" - fi - else - AC_MSG_RESULT([** OpenSSL libraries not found **]) - fi - - fi - - if test "x$ac_ssl_backend" = "x"; then - - if test "x$with_gnutls" != "xno" -a "x$with_gnutls" != "x"; then - CFLAGS="$CFLAGS -I$with_gnutls/include" - LDFLAGS="$LDFLAGS -L$with_gnutls/lib" - AC_MSG_RESULT([Using GnuTLS prefix... $with_gnutls]) - fi - - AC_CHECK_PROGS(PKG_CONFIG, + if test "x$ac_ssl_backend" = "x"; then + # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK + #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1) + AC_DEFINE(PJ_HAS_SSL_SOCK, 1) + AC_DEFINE(PJ_SSL_SOCK_IMP, PJ_SSL_SOCK_IMP_OPENSSL) + ac_ssl_backend="openssl" + fi + else + AC_MSG_RESULT([** OpenSSL libraries not found **]) + fi + fi + + if test "x$ac_ssl_backend" = "x"; then + if test "x$with_gnutls" != "xno" -a "x$with_gnutls" != "x"; then + CFLAGS="$CFLAGS -I$with_gnutls/include" + LDFLAGS="$LDFLAGS -L$with_gnutls/lib" + AC_MSG_RESULT([Using GnuTLS prefix... $with_gnutls]) + fi + + AC_CHECK_PROGS(PKG_CONFIG, $host-pkg-config pkg-config "python pkgconfig.py", none) - AC_MSG_RESULT([checking for GnuTLS installations..]) - AC_SUBST(gnutls_h_present) - AC_SUBST(libgnutls_present) - AC_CHECK_HEADER(gnutls/gnutls.h, [gnutls_h_present=1]) - - if test "$PKG_CONFIG" != "none"; then - if $PKG_CONFIG --exists gnutls; then - LIBS="$LIBS `$PKG_CONFIG --libs gnutls`" - libgnutls_present=1 - else - AC_CHECK_LIB(gnutls, - gnutls_certificate_set_x509_system_trust, - [libgnutls_present=1 && - LIBS="$LIBS -lgnutls"]) - fi - else - AC_MSG_RESULT([*** Warning: neither pkg-config nor python is available, disabling gnutls. ***]) - fi - - if test "x$gnutls_h_present" = "x1" -a "x$libgnutls_present" = "x1"; then - AC_MSG_RESULT([GnuTLS library found, SSL support enabled]) - AC_DEFINE(PJ_HAS_SSL_SOCK, 1) - AC_DEFINE(PJ_SSL_SOCK_IMP, PJ_SSL_SOCK_IMP_GNUTLS) - ac_ssl_backend="gnutls" - else - AC_MSG_RESULT([** No GnuTLS libraries found, disabling SSL support **]) - fi - - fi - ]) + + AC_MSG_RESULT([checking for GnuTLS installations..]) + AC_SUBST(gnutls_h_present) + AC_SUBST(libgnutls_present) + AC_CHECK_HEADER(gnutls/gnutls.h, [gnutls_h_present=1]) + + if test "$PKG_CONFIG" != "none"; then + if $PKG_CONFIG --exists gnutls; then + LIBS="$LIBS `$PKG_CONFIG --libs gnutls`" + libgnutls_present=1 + else + AC_CHECK_LIB(gnutls, gnutls_certificate_set_x509_system_trust, [ + libgnutls_present=1 && LIBS="$LIBS -lgnutls"]) + fi + else + AC_MSG_RESULT([*** Warning: neither pkg-config nor python is available, disabling gnutls. ***]) + fi + + if test "x$gnutls_h_present" = "x1" -a "x$libgnutls_present" = "x1"; then + AC_MSG_RESULT([GnuTLS library found, SSL support enabled]) + AC_DEFINE(PJ_HAS_SSL_SOCK, 1) + AC_DEFINE(PJ_SSL_SOCK_IMP, PJ_SSL_SOCK_IMP_GNUTLS) + ac_ssl_backend="gnutls" + else + AC_MSG_RESULT([** No GnuTLS libraries found, disabling SSL support **]) + fi + + fi + ] +) dnl # Obsolete option --with-opencore-amrnb AC_ARG_WITH(opencore-amrnb, AS_HELP_STRING([--with-opencore-amrnb=DIR], - [This option is obsolete and replaced by --with-opencore-amr=DIR]), + [This option is obsolete and replaced by --with-opencore-amr=DIR]), [AC_MSG_ERROR(This option is obsolete and replaced by --with-opencore-amr=DIR)], [true;] - ) +) dnl # opencore-amr alt prefix AC_ARG_WITH(opencore-amr, - AS_HELP_STRING([--with-opencore-amr=DIR], - [Specify alternate libopencore-amr prefix]), + AS_HELP_STRING([--with-opencore-amr=DIR], [Specify alternate libopencore-amr prefix]), [], [with_opencore_amr=no] - ) +) dnl # Do not use default opencore-amr installation if we are cross-compiling if test "x$ac_cross_compile" != "x" -a "x$with_opencore_amr" = "xno"; then @@ -2077,11 +2296,10 @@ fi dnl # vo-amrwbenc alt prefix AC_ARG_WITH(opencore-amrwbenc, - AS_HELP_STRING([--with-opencore-amrwbenc=DIR], - [Specify alternate libvo-amrwbenc prefix]), + AS_HELP_STRING([--with-opencore-amrwbenc=DIR], [Specify alternate libvo-amrwbenc prefix]), [], [with_opencore_amrwbenc=no] - ) +) dnl # Do not use default vo-amrwbenc installation if we are cross-compiling if test "x$ac_cross_compile" != "x" -a "x$with_opencore_amrwbenc" = "xno"; then @@ -2093,68 +2311,75 @@ dnl # Include opencore-amr support AC_SUBST(ac_no_opencore_amrnb) AC_SUBST(ac_no_opencore_amrwb) AC_ARG_ENABLE(opencore_amr, - AS_HELP_STRING([--disable-opencore-amr], - [Exclude OpenCORE AMR support from the build (default: autodetect)]) - , - [ - if test "$enable_opencore_amr" = "no"; then - [ac_no_opencore_amrnb=1] - [ac_no_opencore_amrwb=1] - AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRNB_CODEC,0) - AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRWB_CODEC,0) - AC_MSG_RESULT([Checking if OpenCORE AMR support is disabled... yes]) - fi - ], - [ - AC_MSG_RESULT([checking for OpenCORE AMR installations..]) - if test "x$with_opencore_amr" != "xno" -a "x$with_opencore_amr" != "x"; then - CFLAGS="$CFLAGS -I$with_opencore_amr/include" - CPPFLAGS="$CPPFLAGS -I$with_opencore_amr/include" - LDFLAGS="$LDFLAGS -L$with_opencore_amr/lib" - AC_MSG_RESULT([Using OpenCORE AMR prefix... $with_opencore_amr]) - fi - if test "x$with_opencore_amrwbenc" != "xno" -a "x$with_opencore_amrwbenc" != "x"; then - CFLAGS="$CFLAGS -I$with_opencore_amrwbenc/include" - CPPFLAGS="$CPPFLAGS -I$with_opencore_amrwbenc/include" - LDFLAGS="$LDFLAGS -L$with_opencore_amrwbenc/lib" - AC_MSG_RESULT([Using OpenCORE AMRWB-enc prefix... $with_opencore_amrwbenc]) - fi - AC_SUBST(opencore_amrnb_h_present) - AC_SUBST(opencore_amrnb_present) - AC_CHECK_HEADER(opencore-amrnb/interf_enc.h,[opencore_amrnb_h_present=1]) - AC_CHECK_LIB(opencore-amrnb,Encoder_Interface_init,[opencore_amrnb_present=1 && LIBS="$LIBS -lopencore-amrnb"]) - if test "x$opencore_amrnb_h_present" = "x1" -a "x$opencore_amrnb_present" = "x1"; then - AC_MSG_RESULT([OpenCORE AMR-NB library found, AMR-NB support enabled]) - AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRNB_CODEC,1) - else - [ac_no_opencore_amrnb=1] - AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRNB_CODEC,0) - fi - AC_SUBST(opencore_amrwb_enc_h_present) - AC_SUBST(opencore_amrwb_enc_present) - AC_SUBST(opencore_amrwb_dec_h_present) - AC_SUBST(opencore_amrwb_dec_present) - AC_CHECK_HEADER(vo-amrwbenc/enc_if.h,[opencore_amrwb_enc_h_present=1]) - AC_CHECK_HEADER(opencore-amrwb/dec_if.h,[opencore_amrwb_dec_h_present=1]) - AC_CHECK_LIB(opencore-amrwb,D_IF_init,[opencore_amrwb_dec_present=1 && LIBS="$LIBS -lopencore-amrwb"]) - AC_CHECK_LIB(vo-amrwbenc,E_IF_init,[opencore_amrwb_enc_present=1 && LIBS="$LIBS -lvo-amrwbenc"]) - if test "x$opencore_amrwb_enc_h_present" = "x1" -a "x$opencore_amrwb_dec_h_present" = "x1" -a "x$opencore_amrwb_enc_present" = "x1" -a "x$opencore_amrwb_dec_present" = "x1"; then - AC_MSG_RESULT([OpenCORE AMR-WB library found, AMR-WB support enabled]) - AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRWB_CODEC,1) - else - [ac_no_opencore_amrwb=1] - AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRWB_CODEC,0) - fi - - ]) + AS_HELP_STRING([--disable-opencore-amr], [Exclude OpenCORE AMR support from the build (default: autodetect)]), + [ + if test "$enable_opencore_amr" = "no"; then + [ac_no_opencore_amrnb=1] + [ac_no_opencore_amrwb=1] + AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRNB_CODEC,0) + AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRWB_CODEC,0) + AC_MSG_RESULT([Checking if OpenCORE AMR support is disabled... yes]) + fi + ], + [ + AC_MSG_RESULT([checking for OpenCORE AMR installations..]) + if test "x$with_opencore_amr" != "xno" -a "x$with_opencore_amr" != "x"; then + CFLAGS="$CFLAGS -I$with_opencore_amr/include" + CPPFLAGS="$CPPFLAGS -I$with_opencore_amr/include" + LDFLAGS="$LDFLAGS -L$with_opencore_amr/lib" + AC_MSG_RESULT([Using OpenCORE AMR prefix... $with_opencore_amr]) + fi + + if test "x$with_opencore_amrwbenc" != "xno" -a "x$with_opencore_amrwbenc" != "x"; then + CFLAGS="$CFLAGS -I$with_opencore_amrwbenc/include" + CPPFLAGS="$CPPFLAGS -I$with_opencore_amrwbenc/include" + LDFLAGS="$LDFLAGS -L$with_opencore_amrwbenc/lib" + AC_MSG_RESULT([Using OpenCORE AMRWB-enc prefix... $with_opencore_amrwbenc]) + fi + + AC_SUBST(opencore_amrnb_h_present) + AC_SUBST(opencore_amrnb_present) + AC_CHECK_HEADER(opencore-amrnb/interf_enc.h,[opencore_amrnb_h_present=1]) + AC_CHECK_LIB(opencore-amrnb,Encoder_Interface_init,[ + opencore_amrnb_present=1 && LIBS="$LIBS -lopencore-amrnb"]) + + if test "x$opencore_amrnb_h_present" = "x1" -a "x$opencore_amrnb_present" = "x1"; then + AC_MSG_RESULT([OpenCORE AMR-NB library found, AMR-NB support enabled]) + AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRNB_CODEC,1) + else + [ac_no_opencore_amrnb=1] + AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRNB_CODEC,0) + fi + + AC_SUBST(opencore_amrwb_enc_h_present) + AC_SUBST(opencore_amrwb_enc_present) + AC_SUBST(opencore_amrwb_dec_h_present) + AC_SUBST(opencore_amrwb_dec_present) + AC_CHECK_HEADER(vo-amrwbenc/enc_if.h,[opencore_amrwb_enc_h_present=1]) + AC_CHECK_HEADER(opencore-amrwb/dec_if.h,[opencore_amrwb_dec_h_present=1]) + + AC_CHECK_LIB(opencore-amrwb,D_IF_init,[ + opencore_amrwb_dec_present=1 && LIBS="$LIBS -lopencore-amrwb"]) + + AC_CHECK_LIB(vo-amrwbenc,E_IF_init,[ + opencore_amrwb_enc_present=1 && LIBS="$LIBS -lvo-amrwbenc"]) + + if test "x$opencore_amrwb_enc_h_present" = "x1" -a "x$opencore_amrwb_dec_h_present" = "x1" -a "x$opencore_amrwb_enc_present" = "x1" -a "x$opencore_amrwb_dec_present" = "x1"; then + AC_MSG_RESULT([OpenCORE AMR-WB library found, AMR-WB support enabled]) + AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRWB_CODEC,1) + else + [ac_no_opencore_amrwb=1] + AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRWB_CODEC,0) + fi + ] +) dnl # SILK prefix AC_ARG_WITH(silk, - AS_HELP_STRING([--with-silk=DIR], - [Specify alternate SILK prefix]), + AS_HELP_STRING([--with-silk=DIR], [Specify alternate SILK prefix]), [], [with_silk=no] - ) +) dnl # Do not use default SILK installation if we are cross-compiling if test "x$ac_cross_compile" != "x" -a "x$with_silk" = "xno"; then @@ -2164,36 +2389,38 @@ fi dnl # Include SILK support AC_SUBST(ac_no_silk) AC_ARG_ENABLE(silk, - AS_HELP_STRING([--disable-silk], - [Exclude SILK support from the build (default: autodetect)]) - , - [ - if test "$enable_silk" = "no"; then - [ac_no_silk=1] - AC_DEFINE(PJMEDIA_HAS_SILK_CODEC,0) - AC_MSG_RESULT([Checking if SILK support is disabled... yes]) - fi - ], - [ - AC_MSG_RESULT([checking for SILK installations..]) - if test "x$with_silk" != "xno" -a "x$with_silk" != "x"; then - CFLAGS="$CFLAGS -I$with_silk/interface" - CPPFLAGS="$CPPFLAGS -I$with_silk/interface" - LDFLAGS="$LDFLAGS -L$with_silk" - AC_MSG_RESULT([Using SILK prefix... $with_silk]) - fi - AC_SUBST(silk_h_present) - AC_SUBST(silk_present) - AC_CHECK_HEADER(SKP_Silk_SDK_API.h,[silk_h_present=1]) - AC_CHECK_LIB(SKP_SILK_SDK,SKP_Silk_SDK_get_version,[silk_present=1 && LIBS="$LIBS -lSKP_SILK_SDK"]) - if test "x$silk_h_present" = "x1" -a "x$silk_present" = "x1"; then - AC_MSG_RESULT([SILK library found, SILK support enabled]) - AC_DEFINE(PJMEDIA_HAS_SILK_CODEC,1) - else - [ac_no_silk=1] - AC_DEFINE(PJMEDIA_HAS_SILK_CODEC,0) - fi - ]) + AS_HELP_STRING([--disable-silk], [Exclude SILK support from the build (default: autodetect)]), + [ + if test "$enable_silk" = "no"; then + [ac_no_silk=1] + AC_DEFINE(PJMEDIA_HAS_SILK_CODEC,0) + AC_MSG_RESULT([Checking if SILK support is disabled... yes]) + fi + ], + [ + AC_MSG_RESULT([checking for SILK installations..]) + if test "x$with_silk" != "xno" -a "x$with_silk" != "x"; then + CFLAGS="$CFLAGS -I$with_silk/interface" + CPPFLAGS="$CPPFLAGS -I$with_silk/interface" + LDFLAGS="$LDFLAGS -L$with_silk" + AC_MSG_RESULT([Using SILK prefix... $with_silk]) + fi + + AC_SUBST(silk_h_present) + AC_SUBST(silk_present) + AC_CHECK_HEADER(SKP_Silk_SDK_API.h,[silk_h_present=1]) + AC_CHECK_LIB(SKP_SILK_SDK,SKP_Silk_SDK_get_version,[ + silk_present=1 && LIBS="$LIBS -lSKP_SILK_SDK"]) + + if test "x$silk_h_present" = "x1" -a "x$silk_present" = "x1"; then + AC_MSG_RESULT([SILK library found, SILK support enabled]) + AC_DEFINE(PJMEDIA_HAS_SILK_CODEC,1) + else + [ac_no_silk=1] + AC_DEFINE(PJMEDIA_HAS_SILK_CODEC,0) + fi + ] +) dnl # Do not use default OPUS installation if we are cross-compiling if test "x$ac_cross_compile" != "x" -a "x$with_opus" = "xno"; then @@ -2202,54 +2429,53 @@ fi dnl # OPUS prefix AC_ARG_WITH(opus, - AS_HELP_STRING([--with-opus=DIR], - [Specify alternate OPUS prefix]), + AS_HELP_STRING([--with-opus=DIR], [Specify alternate OPUS prefix]), [], [with_opus=no] - ) +) dnl # Include OPUS support AC_SUBST(ac_no_opus) AC_ARG_ENABLE(opus, - AS_HELP_STRING([--disable-opus], - [Exclude OPUS support from the build (default: autodetect)]) - , - [ - if test "$enable_opus" = "no"; then - [ac_no_opus=1] - AC_DEFINE(PJMEDIA_HAS_OPUS_CODEC,0) - AC_MSG_RESULT([Checking if OPUS support is disabled... yes]) - fi - ], - [ - AC_MSG_RESULT([checking for OPUS installations..]) - if test "x$with_opus" != "xno" -a "x$with_opus" != "x"; then - CFLAGS="$CFLAGS -I$with_opus/include" - CPPFLAGS="$CPPFLAGS -I$with_opus/include" - LDFLAGS="$LDFLAGS -L$with_opus/lib" - AC_MSG_RESULT([Using OPUS prefix... $with_opus]) - fi - AC_SUBST(opus_h_present) - AC_SUBST(opus_present) - AC_CHECK_HEADER(opus/opus.h,[opus_h_present=1]) - AC_CHECK_LIB(opus,opus_repacketizer_get_size,[opus_present=1 && LIBS="-lopus $LIBS"]) - if test "x$opus_h_present" = "x1" -a "x$opus_present" = "x1"; then - AC_MSG_RESULT([OPUS library found, OPUS support enabled]) - AC_DEFINE(PJMEDIA_HAS_OPUS_CODEC,1) - else - [ac_no_opus=1] - AC_MSG_RESULT([OPUS library not found, OPUS support disabled]) - AC_DEFINE(PJMEDIA_HAS_OPUS_CODEC,0) - fi - ]) + AS_HELP_STRING([--disable-opus], [Exclude OPUS support from the build (default: autodetect)]), + [ + if test "$enable_opus" = "no"; then + [ac_no_opus=1] + AC_DEFINE(PJMEDIA_HAS_OPUS_CODEC,0) + AC_MSG_RESULT([Checking if OPUS support is disabled... yes]) + fi + ], + [ + AC_MSG_RESULT([checking for OPUS installations..]) + if test "x$with_opus" != "xno" -a "x$with_opus" != "x"; then + CFLAGS="$CFLAGS -I$with_opus/include" + CPPFLAGS="$CPPFLAGS -I$with_opus/include" + LDFLAGS="$LDFLAGS -L$with_opus/lib" + AC_MSG_RESULT([Using OPUS prefix... $with_opus]) + fi + AC_SUBST(opus_h_present) + AC_SUBST(opus_present) + AC_CHECK_HEADER(opus/opus.h,[opus_h_present=1]) + AC_CHECK_LIB(opus,opus_repacketizer_get_size,[ + opus_present=1 && LIBS="-lopus $LIBS"]) + + if test "x$opus_h_present" = "x1" -a "x$opus_present" = "x1"; then + AC_MSG_RESULT([OPUS library found, OPUS support enabled]) + AC_DEFINE(PJMEDIA_HAS_OPUS_CODEC,1) + else + [ac_no_opus=1] + AC_MSG_RESULT([OPUS library not found, OPUS support disabled]) + AC_DEFINE(PJMEDIA_HAS_OPUS_CODEC,0) + fi + ] +) dnl # bcg729 prefix AC_ARG_WITH(bcg729, - AS_HELP_STRING([--with-bcg729=DIR], - [Specify alternate bcg729 prefix]), - [], - [with_bcg729=no] - ) + AS_HELP_STRING([--with-bcg729=DIR], [Specify alternate bcg729 prefix]), + [], + [with_bcg729=no] +) dnl # Do not use default bcg729 installation if we are cross-compiling if test "x$ac_cross_compile" != "x" -a "x$with_bcg729" = "xno"; then @@ -2259,65 +2485,66 @@ fi dnl # bcg729 AC_SUBST(ac_no_bcg729) AC_ARG_ENABLE(bcg729, - AS_HELP_STRING([--disable-bcg729], - [Disable bcg729 (default: not disabled)]), - [ - if test "$enable_bcg729" = "no"; then - [ac_no_bcg729=1] - AC_DEFINE(PJMEDIA_HAS_BCG729,0) - AC_MSG_RESULT([Checking if bcg729 is disabled... yes]) - fi - ], - [ - if test "x$with_bcg729" != "xno" -a "x$with_bcg729" != "x"; then - BCG729_PREFIX=$with_bcg729 - BCG729_CFLAGS="-I$BCG729_PREFIX/include" - BCG729_LDFLAGS="-L$BCG729_PREFIX/lib" - AC_MSG_RESULT([Using bcg729 prefix... $with_bcg729]) - else - BCG729_CFLAGS="" - BCG729_LDFLAGS="" - fi - - AC_MSG_CHECKING([bcg729 usability]) - - BCG729_LIBS="-lbcg729" - - SAVED_LIBS="$LIBS" - SAVED_LDFLAGS="$LDFLAGS" - SAVED_CFLAGS="$CFLAGS" - - LIBS="$BCG729_LIBS $LIBS" - LDFLAGS="$BCG729_LDFLAGS $LDFLAGS" - CFLAGS="$BCG729_CFLAGS $CFLAGS" - - AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include - #include - ]], - [initBcg729EncoderChannel(0);] - )], - [ - AC_DEFINE(PJMEDIA_HAS_BCG729,1) - AC_MSG_RESULT(yes) - ], - [ - [ac_no_bcg729=1] - AC_DEFINE(PJMEDIA_HAS_BCG729,0) - LIBS="$SAVED_LIBS" - LDFLAGS="$SAVED_LDFLAGS" - CFLAGS="$SAVED_CFLAGS" - AC_MSG_RESULT(no) - ]) - - ]) - + AS_HELP_STRING([--disable-bcg729], [Disable bcg729 (default: not disabled)]), + [ + if test "$enable_bcg729" = "no"; then + [ac_no_bcg729=1] + AC_DEFINE(PJMEDIA_HAS_BCG729,0) + AC_MSG_RESULT([Checking if bcg729 is disabled... yes]) + fi + ], + [ + if test "x$with_bcg729" != "xno" -a "x$with_bcg729" != "x"; then + BCG729_PREFIX=$with_bcg729 + BCG729_CFLAGS="-I$BCG729_PREFIX/include" + BCG729_LDFLAGS="-L$BCG729_PREFIX/lib" + AC_MSG_RESULT([Using bcg729 prefix... $with_bcg729]) + else + BCG729_CFLAGS="" + BCG729_LDFLAGS="" + fi + + AC_MSG_CHECKING([bcg729 usability]) + + BCG729_LIBS="-lbcg729" + + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CFLAGS="$CFLAGS" + + LIBS="$BCG729_LIBS $LIBS" + LDFLAGS="$BCG729_LDFLAGS $LDFLAGS" + CFLAGS="$BCG729_CFLAGS $CFLAGS" + + AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include + #include ]], + [initBcg729EncoderChannel(0);]) + ], + [ + AC_DEFINE(PJMEDIA_HAS_BCG729,1) + AC_MSG_RESULT(yes) + ], + [ + [ac_no_bcg729=1] + AC_DEFINE(PJMEDIA_HAS_BCG729,0) + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CFLAGS="$SAVED_CFLAGS" + AC_MSG_RESULT(no) + ] + ) + ] +) + dnl # lyra prefix AC_ARG_WITH(lyra, - AS_HELP_STRING([--with-lyra=DIR], - [Specify alternate lyra prefix]), - [], - [with_lyra=no] - ) + AS_HELP_STRING([--with-lyra=DIR], [Specify alternate lyra prefix]), + [], + [with_lyra=no] +) dnl # Do not use default lyra installation if we are cross-compiling if test "x$ac_cross_compile" != "x" -a "x$with_lyra" = "xno"; then @@ -2328,93 +2555,96 @@ dnl # lyra AC_SUBST(ac_no_lyra_codec) AC_SUBST(ac_lyra_model_path) AC_ARG_ENABLE(lyra, - AS_HELP_STRING([--disable-lyra], - [Disable lyra (default: not disabled)]), - [ - if test "$enable_lyra" = "no"; then - [ac_no_lyra_codec=1] - AC_DEFINE(PJMEDIA_HAS_LYRA_CODEC,0) - AC_MSG_RESULT([Checking if lyra is disabled... yes]) - fi - ], - [ - if test "x$with_lyra" != "xno" -a "x$with_lyra" != "x"; then - LYRA_PREFIX=$with_lyra - dnl # inside autoconf, single quoted preprocessor definition will raise error - LYRA_CPPFLAGS="-DGLOG_DEPRECATED=__attribute__((deprecated)) -DGLOG_EXPORT=__attribute__((visibility(\"default\"))) -DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))" - LYRA_CXXFLAGS="-std=c++17 -Wno-deprecated-builtins -I$LYRA_PREFIX/include/com_google_absl -I$LYRA_PREFIX/include/gulrak_filesystem -I$LYRA_PREFIX/include/com_google_glog/src -I$LYRA_PREFIX" - LYRA_LDFLAGS="-L$LYRA_PREFIX/lib" - AC_MSG_RESULT([Using lyra prefix... $with_lyra]) - else - LYRA_CXXFLAGS="" - LYRA_LDFLAGS="" - fi - - AC_MSG_CHECKING([lyra usability]) - - LYRA_LIBS="-llyra" - - SAVED_LIBS="$LIBS" - SAVED_LDFLAGS="$LDFLAGS" - SAVED_CXXFLAGS="$CXXFLAGS" - SAVED_CPPFLAGS="$CPPFLAGS" - - LIBS="$LYRA_LIBS $LIBS" - LDFLAGS="$LYRA_LDFLAGS $LDFLAGS" - CXXFLAGS="$LYRA_CXXFLAGS $CXXFLAGS" - CPPFLAGS="$LYRA_CPPFLAGS $CPPFLAGS" - - AC_LANG_PUSH([C++]) - AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include "lyra_decoder.h" - ]], - [std::unique_ptr dec = chromemedia::codec::LyraDecoder::Create(8000,1,"");] - )], - [ - [ac_lyra_model_path="$LYRA_PREFIX/model_coeffs"] - AC_DEFINE(PJMEDIA_HAS_LYRA_CODEC,1) - dnl # use single quoted preprocessor definition - LYRA_CPPFLAGS="'-DGLOG_DEPRECATED=__attribute__((deprecated))' '-DGLOG_EXPORT=__attribute__((visibility(\"default\")))' '-DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))'" - CXXFLAGS="$LYRA_CPPFLAGS $CXXFLAGS" - AC_MSG_RESULT(yes) - ], - [ - [ac_no_lyra_codec=1] - LIBS="$SAVED_LIBS" - LDFLAGS="$SAVED_LDFLAGS" - CXXFLAGS="$SAVED_CXXFLAGS" - AC_MSG_RESULT(no) - ]) - AC_LANG_POP([C++]) - CPPFLAGS="$SAVED_CPPFLAGS" - ]) - - + AS_HELP_STRING([--disable-lyra], [Disable lyra (default: not disabled)]), + [ + if test "$enable_lyra" = "no"; then + [ac_no_lyra_codec=1] + AC_DEFINE(PJMEDIA_HAS_LYRA_CODEC,0) + AC_MSG_RESULT([Checking if lyra is disabled... yes]) + fi + ], + [ + if test "x$with_lyra" != "xno" -a "x$with_lyra" != "x"; then + LYRA_PREFIX=$with_lyra + dnl # inside autoconf, single quoted preprocessor definition will raise error + LYRA_CPPFLAGS="-DGLOG_DEPRECATED=__attribute__((deprecated)) -DGLOG_EXPORT=__attribute__((visibility(\"default\"))) -DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))" + LYRA_CXXFLAGS="-std=c++17 -Wno-deprecated-builtins -I$LYRA_PREFIX/include/com_google_absl -I$LYRA_PREFIX/include/gulrak_filesystem -I$LYRA_PREFIX/include/com_google_glog/src -I$LYRA_PREFIX" + LYRA_LDFLAGS="-L$LYRA_PREFIX/lib" + AC_MSG_RESULT([Using lyra prefix... $with_lyra]) + else + LYRA_CXXFLAGS="" + LYRA_LDFLAGS="" + fi + + AC_MSG_CHECKING([lyra usability]) + + LYRA_LIBS="-llyra" + + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CXXFLAGS="$CXXFLAGS" + SAVED_CPPFLAGS="$CPPFLAGS" + + LIBS="$LYRA_LIBS $LIBS" + LDFLAGS="$LYRA_LDFLAGS $LDFLAGS" + CXXFLAGS="$LYRA_CXXFLAGS $CXXFLAGS" + CPPFLAGS="$LYRA_CPPFLAGS $CPPFLAGS" + + AC_LANG_PUSH([C++]) + AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM([ + [#include "lyra_decoder.h"]], + [std::unique_ptr dec = chromemedia::codec::LyraDecoder::Create(8000,1,"");]) + ], + [ + [ac_lyra_model_path="$LYRA_PREFIX/model_coeffs"] + AC_DEFINE(PJMEDIA_HAS_LYRA_CODEC,1) + dnl # use single quoted preprocessor definition + LYRA_CPPFLAGS="'-DGLOG_DEPRECATED=__attribute__((deprecated))' '-DGLOG_EXPORT=__attribute__((visibility(\"default\")))' '-DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))'" + CXXFLAGS="$LYRA_CPPFLAGS $CXXFLAGS" + AC_MSG_RESULT(yes) + ], + [ + [ac_no_lyra_codec=1] + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CXXFLAGS="$SAVED_CXXFLAGS" + AC_MSG_RESULT(no) + ] + ) + AC_LANG_POP([C++]) + CPPFLAGS="$SAVED_CPPFLAGS" + ] +) dnl # Include libsrtp AC_SUBST(ac_no_srtp) AC_ARG_ENABLE(libsrtp, - AS_HELP_STRING([--disable-libsrtp], - [Exclude libsrtp in the build]), - [if test "$enable_libsrtp" = "no"; then - [ac_no_srtp=1] - CFLAGS="$CFLAGS -DPJMEDIA_HAS_SRTP=0" - AC_MSG_RESULT([Checking if libsrtp is disabled...yes]) - fi], - AC_MSG_RESULT([Checking if libsrtp is disabled...no])) - + AS_HELP_STRING([--disable-libsrtp], [Exclude libsrtp in the build]), + [ + if test "$enable_libsrtp" = "no"; then + [ac_no_srtp=1] + CFLAGS="$CFLAGS -DPJMEDIA_HAS_SRTP=0" + AC_MSG_RESULT([Checking if libsrtp is disabled...yes]) + fi + ], + AC_MSG_RESULT([Checking if libsrtp is disabled...no]) +) dnl # Include libyuv AC_SUBST(ac_no_yuv) AC_ARG_ENABLE(libyuv, - AS_HELP_STRING([--disable-libyuv], - [Exclude libyuv in the build]), - [if test "$enable_libyuv" = "no"; then - [ac_no_yuv=1] - AC_DEFINE(PJMEDIA_HAS_LIBYUV,0) - AC_MSG_RESULT([Checking if libyuv is disabled...yes]) - fi], - AC_MSG_RESULT([Checking if libyuv is disabled...no])) - + AS_HELP_STRING([--disable-libyuv], [Exclude libyuv in the build]), + [ + if test "$enable_libyuv" = "no"; then + [ac_no_yuv=1] + AC_DEFINE(PJMEDIA_HAS_LIBYUV,0) + AC_MSG_RESULT([Checking if libyuv is disabled...yes]) + fi + ], + AC_MSG_RESULT([Checking if libyuv is disabled...no]) +) dnl # Include webrtc AC_SUBST(ac_no_webrtc) @@ -2422,81 +2652,83 @@ AC_SUBST(ac_webrtc_instset) AC_SUBST(ac_webrtc_cflags) AC_SUBST(ac_webrtc_ldflags) AC_ARG_ENABLE(libwebrtc, - AS_HELP_STRING([--disable-libwebrtc], - [Exclude libwebrtc in the build]), - [if test "$enable_libwebrtc" = "no"; then - [ac_no_webrtc=1] - AC_DEFINE(PJMEDIA_HAS_LIBWEBRTC,0) - AC_MSG_RESULT([Checking if libwebrtc is disabled...yes]) - fi], - [ - AC_MSG_RESULT([Checking if libwebrtc is disabled...no]) - case $target in - *-apple-darwin_ios*) - case $target in - arm64*) - ac_webrtc_instset=neon - ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" - ;; - *arm*) - ac_webrtc_instset=neon - ;; - *) - ac_webrtc_instset=sse2 - ;; - esac - ;; - *android*) - case $TARGET_ABI in - armeabi-v7a) - ac_webrtc_instset=neon - ac_webrtc_cflags="-mfloat-abi=softfp -mfpu=neon" - ;; - armeabi) - ac_webrtc_instset=neon - ac_webrtc_cflags="-mthumb -mfloat-abi=softfp -mfpu=neon -march=armv7" - ;; - arm64*) - ac_webrtc_instset=neon - ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" - ;; - mips64*) - ac_webrtc_instset=generic - ;; - mips*) - ac_webrtc_instset=mips - ;; - x86*) - ac_webrtc_instset=sse2 - ;; - *) - ac_webrtc_instset=generic - ;; - esac - ;; - *mingw* | *cygw*) - ac_webrtc_instset=sse2 - ac_webrtc_cflags="-msse2" - ;; - *win32* | *w32* | *darwin* | *linux*) - case $target in - armv7l*gnueabihf) - ac_webrtc_instset=neon - ac_webrtc_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" - ;; - arm-apple-darwin* | aarch64*) - ac_webrtc_instset=neon - ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" - ;; - *) - ac_webrtc_instset=sse2 - ;; - esac - ;; - *) - ;; - esac - ]) + AS_HELP_STRING([--disable-libwebrtc], [Exclude libwebrtc in the build]), + [ + if test "$enable_libwebrtc" = "no"; then + [ac_no_webrtc=1] + AC_DEFINE(PJMEDIA_HAS_LIBWEBRTC,0) + AC_MSG_RESULT([Checking if libwebrtc is disabled...yes]) + fi + ], + [ + AC_MSG_RESULT([Checking if libwebrtc is disabled...no]) + case $target in + *-apple-darwin_ios*) + case $target in + arm64*) + ac_webrtc_instset=neon + ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" + ;; + *arm*) + ac_webrtc_instset=neon + ;; + *) + ac_webrtc_instset=sse2 + ;; + esac + ;; + *android*) + case $TARGET_ABI in + armeabi-v7a) + ac_webrtc_instset=neon + ac_webrtc_cflags="-mfloat-abi=softfp -mfpu=neon" + ;; + armeabi) + ac_webrtc_instset=neon + ac_webrtc_cflags="-mthumb -mfloat-abi=softfp -mfpu=neon -march=armv7" + ;; + arm64*) + ac_webrtc_instset=neon + ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" + ;; + mips64*) + ac_webrtc_instset=generic + ;; + mips*) + ac_webrtc_instset=mips + ;; + x86*) + ac_webrtc_instset=sse2 + ;; + *) + ac_webrtc_instset=generic + ;; + esac + ;; + *mingw* | *cygw*) + ac_webrtc_instset=sse2 + ac_webrtc_cflags="-msse2" + ;; + *win32* | *w32* | *darwin* | *linux*) + case $target in + armv7l*gnueabihf) + ac_webrtc_instset=neon + ac_webrtc_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" + ;; + arm-apple-darwin* | aarch64*) + ac_webrtc_instset=neon + ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" + ;; + *) + ac_webrtc_instset=sse2 + ;; + esac + ;; + *) + ;; + esac + ] +) dnl # Include webrtc AEC3 @@ -2505,104 +2737,112 @@ AC_SUBST(ac_webrtc_aec3_instset) AC_SUBST(ac_webrtc_aec3_cflags) AC_SUBST(ac_webrtc_aec3_ldflags) AC_ARG_ENABLE(libwebrtc_aec3, - AS_HELP_STRING([--enable-libwebrtc-aec3], - [Build libwebrtc-aec3 that's included in PJSIP]), - [ - if test "$enable_libwebrtc_aec3" = "yes"; then - # Test if we can build WebRtc AEC3 - AC_MSG_CHECKING([if WebRtc AEC3 can be compiled with C++17]) - - SAVED_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS -std=c++17" - - AC_LANG_PUSH([C++]) - AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [])], - [AC_MSG_RESULT(yes) - LD="$CXX"], - [AC_MSG_RESULT(no) - CXXFLAGS="$SAVED_CXXFLAGS" - ac_no_webrtc_aec3=1 - AC_DEFINE(PJMEDIA_HAS_LIBWEBRTC_AEC3,0)]) - AC_LANG_POP([C++]) - - case $target in - *-apple-darwin_ios*) - case $target in - arm64*) - ac_webrtc_aec3_instset=neon - ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" - ;; - *arm*) - ac_webrtc_aec3_instset=neon - ;; - *) - ac_webrtc_aec3_instset=sse2 - ;; - esac - ac_webrtc_aec3_cflags+=" -DWEBRTC_IOS=1" - ;; - *android*) - case $TARGET_ABI in - armeabi-v7a) - ac_webrtc_aec3_instset=neon - ac_webrtc_aec3_cflags="-mfloat-abi=softfp -mfpu=neon" - ;; - armeabi) - ac_webrtc_aec3_instset=neon - ac_webrtc_aec3_cflags="-mthumb -mfloat-abi=softfp -mfpu=neon -march=armv7" - ;; - arm64*) - ac_webrtc_aec3_instset=neon - ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" - ;; - x86*) - ac_webrtc_aec3_instset=sse2 - ;; - esac - ac_webrtc_aec3_cflags+=" -DWEBRTC_ANDROID=1" - ;; - *mingw* | *cygw*) - ac_webrtc_aec3_instset=sse2 - ac_webrtc_aec3_cflags="-msse2" - ;; - *win32* | *w32* | *darwin* | *linux*) - case $target in - armv7l*gnueabihf) - ac_webrtc_aec3_instset=neon - ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" - ;; - arm-apple-darwin* | aarch64*) - ac_webrtc_aec3_instset=neon - ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" - ;; - *) - ac_webrtc_aec3_instset=sse2 - ;; - esac - case $target in - *darwin*) - ac_webrtc_aec3_cflags+=" -DWEBRTC_MAC=1" - ;; - *linux*) - ac_webrtc_aec3_cflags+=" -DWEBRTC_LINUX=1" - ;; - esac - ;; - *) - ;; - esac - ac_webrtc_aec3_cflags+=" -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_POSIX=1" - else - ac_no_webrtc_aec3=1 - AC_DEFINE(PJMEDIA_HAS_LIBWEBRTC_AEC3,0) - AC_MSG_RESULT([Checking if libwebrtc-aec3 is enabled...no]) - fi - ], - [ac_no_webrtc_aec3=1 - AC_DEFINE(PJMEDIA_HAS_LIBWEBRTC_AEC3,0) - AC_MSG_RESULT([Checking if libwebrtc-aec3 is enabled...no]) - ]) - + AS_HELP_STRING([--enable-libwebrtc-aec3], [Build libwebrtc-aec3 that's included in PJSIP]), + [ + if test "$enable_libwebrtc_aec3" = "yes"; then + # Test if we can build WebRtc AEC3 + AC_MSG_CHECKING([if WebRtc AEC3 can be compiled with C++17]) + + SAVED_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -std=c++17" + + AC_LANG_PUSH([C++]) + AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM([[]], []) + ], + [ + AC_MSG_RESULT(yes) + LD="$CXX" + ], + [ + AC_MSG_RESULT(no) + CXXFLAGS="$SAVED_CXXFLAGS" + ac_no_webrtc_aec3=1 + AC_DEFINE(PJMEDIA_HAS_LIBWEBRTC_AEC3,0) + ] + ) + AC_LANG_POP([C++]) + + case $target in + *-apple-darwin_ios*) + case $target in + arm64*) + ac_webrtc_aec3_instset=neon + ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" + ;; + *arm*) + ac_webrtc_aec3_instset=neon + ;; + *) + ac_webrtc_aec3_instset=sse2 + ;; + esac + ac_webrtc_aec3_cflags+=" -DWEBRTC_IOS=1" + ;; + *android*) + case $TARGET_ABI in + armeabi-v7a) + ac_webrtc_aec3_instset=neon + ac_webrtc_aec3_cflags="-mfloat-abi=softfp -mfpu=neon" + ;; + armeabi) + ac_webrtc_aec3_instset=neon + ac_webrtc_aec3_cflags="-mthumb -mfloat-abi=softfp -mfpu=neon -march=armv7" + ;; + arm64*) + ac_webrtc_aec3_instset=neon + ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" + ;; + x86*) + ac_webrtc_aec3_instset=sse2 + ;; + esac + ac_webrtc_aec3_cflags+=" -DWEBRTC_ANDROID=1" + ;; + *mingw* | *cygw*) + ac_webrtc_aec3_instset=sse2 + ac_webrtc_aec3_cflags="-msse2" + ;; + *win32* | *w32* | *darwin* | *linux*) + case $target in + armv7l*gnueabihf) + ac_webrtc_aec3_instset=neon + ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" + ;; + arm-apple-darwin* | aarch64*) + ac_webrtc_aec3_instset=neon + ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" + ;; + *) + ac_webrtc_aec3_instset=sse2 + ;; + esac + case $target in + *darwin*) + ac_webrtc_aec3_cflags+=" -DWEBRTC_MAC=1" + ;; + *linux*) + ac_webrtc_aec3_cflags+=" -DWEBRTC_LINUX=1" + ;; + esac + ;; + *) + ;; + esac + ac_webrtc_aec3_cflags+=" -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_POSIX=1" + else + ac_no_webrtc_aec3=1 + AC_DEFINE(PJMEDIA_HAS_LIBWEBRTC_AEC3,0) + AC_MSG_RESULT([Checking if libwebrtc-aec3 is enabled...no]) + fi + ], + [ + ac_no_webrtc_aec3=1 + AC_DEFINE(PJMEDIA_HAS_LIBWEBRTC_AEC3,0) + AC_MSG_RESULT([Checking if libwebrtc-aec3 is enabled...no]) + ] +) dnl ########################################## dnl # @@ -2615,13 +2855,15 @@ dnl # correct value (max_fd+1). If zero, nfds will be filled up with dnl # PJ_FD_SETSIZE AC_MSG_CHECKING([if select() needs correct nfds]) case $target in - *rtems*) AC_DEFINE(PJ_SELECT_NEEDS_NFDS,1) - AC_MSG_RESULT(yes) - ;; - *) AC_DEFINE(PJ_SELECT_NEEDS_NFDS,0) - AC_MSG_RESULT([no (default)]) - AC_MSG_RESULT([** Decided that select() doesn't need correct nfds (please check)]) - ;; + *rtems*) + AC_DEFINE(PJ_SELECT_NEEDS_NFDS,1) + AC_MSG_RESULT(yes) + ;; + *) + AC_DEFINE(PJ_SELECT_NEEDS_NFDS,0) + AC_MSG_RESULT([no (default)]) + AC_MSG_RESULT([** Decided that select() doesn't need correct nfds (please check)]) + ;; esac dnl # Determine if pj_thread_create() should enforce thread stack size when @@ -2629,46 +2871,52 @@ dnl # creating thread. Default is zero, to allow OS to allocate appropriate dnl # thread's stack size. AC_MSG_CHECKING([if pj_thread_create() should enforce stack size]) case $target in - *rtems*) AC_DEFINE(PJ_THREAD_SET_STACK_SIZE,1) - AC_MSG_RESULT(yes) - ;; - *) AC_DEFINE(PJ_THREAD_SET_STACK_SIZE,0) - AC_MSG_RESULT([no (default)]) - ;; + *rtems*) + AC_DEFINE(PJ_THREAD_SET_STACK_SIZE,1) + AC_MSG_RESULT(yes) + ;; + *) + AC_DEFINE(PJ_THREAD_SET_STACK_SIZE,0) + AC_MSG_RESULT([no (default)]) + ;; esac dnl # Determine if pj_thread_create() should allocate thread's stack from dnl # the pool. Default is zero, to let OS allocate thread's stack. AC_MSG_CHECKING([if pj_thread_create() should allocate stack]) case $target in - *rtems*) AC_DEFINE(PJ_THREAD_ALLOCATE_STACK,1) - AC_MSG_RESULT(yes) - ;; - *) AC_DEFINE(PJ_THREAD_ALLOCATE_STACK,0) - AC_MSG_RESULT([no (default)]) - ;; + *rtems*) + AC_DEFINE(PJ_THREAD_ALLOCATE_STACK,1) + AC_MSG_RESULT(yes) + ;; + *) + AC_DEFINE(PJ_THREAD_ALLOCATE_STACK,0) + AC_MSG_RESULT([no (default)]) + ;; esac dnl # This value specifies the value set in errno by the OS when a non-blocking dnl # socket recv() can not return immediate data. case $target in - *mingw* | *cygw* | *win32* | *w32* ) - AC_DEFINE(PJ_BLOCKING_ERROR_VAL,WSAEWOULDBLOCK) - ;; - *) AC_DEFINE(PJ_BLOCKING_ERROR_VAL,EAGAIN) - AC_MSG_RESULT([** Setting non-blocking recv() retval to EAGAIN (please check)]) - ;; + *mingw* | *cygw* | *win32* | *w32* ) + AC_DEFINE(PJ_BLOCKING_ERROR_VAL,WSAEWOULDBLOCK) + ;; + *) + AC_DEFINE(PJ_BLOCKING_ERROR_VAL,EAGAIN) + AC_MSG_RESULT([** Setting non-blocking recv() retval to EAGAIN (please check)]) + ;; esac dnl # This value specifies the value set in errno by the OS when a non-blocking dnl # socket connect() can not get connected immediately. case $target in - *mingw* | *cygw* | *win32* | *w32* ) - AC_DEFINE(PJ_BLOCKING_CONNECT_ERROR_VAL,WSAEWOULDBLOCK) - ;; - *) AC_DEFINE(PJ_BLOCKING_CONNECT_ERROR_VAL,EINPROGRESS) - AC_MSG_RESULT([** Setting non-blocking connect() retval to EINPROGRESS (please check)]) - ;; + *mingw* | *cygw* | *win32* | *w32* ) + AC_DEFINE(PJ_BLOCKING_CONNECT_ERROR_VAL,WSAEWOULDBLOCK) + ;; + *) + AC_DEFINE(PJ_BLOCKING_CONNECT_ERROR_VAL,EINPROGRESS) + AC_MSG_RESULT([** Setting non-blocking connect() retval to EINPROGRESS (please check)]) + ;; esac dnl @@ -2678,11 +2926,11 @@ dnl # pthread_setname_np()/pthread_set_name_np() in pthread_np.h on bsd dnl case $target in - *linux* | *darwin* | *bsd*) - AC_CHECK_HEADER(pthread_np.h,[AC_DEFINE(PJ_HAS_PTHREAD_NP_H,1)]) - AC_CHECK_FUNC(pthread_setname_np,[AC_DEFINE(PJ_HAS_PTHREAD_SETNAME_NP,1)]) - AC_CHECK_FUNC(pthread_set_name_np,[AC_DEFINE(PJ_HAS_PTHREAD_SET_NAME_NP,1)]) - ;; + *linux* | *darwin* | *bsd*) + AC_CHECK_HEADER(pthread_np.h,[AC_DEFINE(PJ_HAS_PTHREAD_NP_H,1)]) + AC_CHECK_FUNC(pthread_setname_np,[AC_DEFINE(PJ_HAS_PTHREAD_SETNAME_NP,1)]) + AC_CHECK_FUNC(pthread_set_name_np,[AC_DEFINE(PJ_HAS_PTHREAD_SET_NAME_NP,1)]) + ;; esac @@ -2690,19 +2938,18 @@ AC_SUBST(target) AC_SUBST(ac_host,unix) AC_SUBST(ac_main_obj) case $target in - *rtems*) - ac_main_obj=main_rtems.o - ;; - *) - ac_main_obj=main.o - ;; + *rtems*) + ac_main_obj=main_rtems.o + ;; + *) + ac_main_obj=main.o + ;; esac AC_SUBST(CC) ac_build_mak_vars=`echo $ac_build_mak_vars | sed 's/\\\\n/\n/g'` AC_OUTPUT - AC_MSG_RESULT([ Configurations for current target have been written to 'build.mak', and 'os-auto.mak' in various build directories, and pjlib/include/pj/compat/os_auto.h. From d874eaae36ff9e9a63161eeda7ea2e05b1ae156b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=9B=D1=83=D1=85?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2?= Date: Wed, 13 Nov 2024 12:49:33 +0300 Subject: [PATCH 103/491] Add blame ignore for https://github.com/pjsip/pjproject/pull/4154 --- .git-blame-ignore-revs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index d929d2d7dc..a82564dd26 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,2 +1,5 @@ # expand tab to spaces and remove $Id$ (#3292) -5ac9104514499d648f68991ef796368c51b4dfec \ No newline at end of file +5ac9104514499d648f68991ef796368c51b4dfec + +# expand tab to spaces and cleanup formatting in aconfigure.ac (#4154) +22464d7a052ab5b7775496bb52211ea66dc2d62d From 4a8d180529d6ffb0760838b1f8cadc4cb5f7ac03 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 14 Nov 2024 05:51:54 +0300 Subject: [PATCH 104/491] Correct cpu features detection during cross-compiation (#4151) --- aconfigure | 70 +++++++++++++++++++++++++++++++++++++++++++++------ aconfigure.ac | 57 +++++++++++++++++++++++++++++++++++------ 2 files changed, 111 insertions(+), 16 deletions(-) diff --git a/aconfigure b/aconfigure index cead0631dc..cf6185c869 100755 --- a/aconfigure +++ b/aconfigure @@ -10535,6 +10535,36 @@ printf "%s\n" "Checking if libyuv is disabled...no" >&6; } fi +SAVED_CFLAGS="$CFLAGS" +case $target_cpu in + arm*) + CFLAGS="-mfpu=neon $CFLAGS" + ;; + aarch64*) + CFLAGS="-march=armv8-a+simd $CFLAGS" + ;; +esac +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ax_cv_support_neon_ext=yes +else $as_nop + ax_cv_support_neon_ext=no + +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +CFLAGS="$SAVED_CFLAGS" + @@ -10604,11 +10634,23 @@ printf "%s\n" "Checking if libwebrtc is disabled...no" >&6; } ;; *win32* | *w32* | *darwin* | *linux*) case $target in - armv7l*gnueabihf) - ac_webrtc_instset=neon - ac_webrtc_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" + arm*gnueabihf) + if test "x$ax_cv_support_neon_ext" = "xyes"; then + ac_webrtc_instset=neon + ac_webrtc_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" + else + ac_webrtc_instset=generic + fi ;; - arm-apple-darwin* | aarch64*) + aarch64*) + if test "x$ax_cv_support_neon_ext" = "xyes"; then + ac_webrtc_instset=neon + ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" + else + ac_webrtc_instset=generic + fi + ;; + arm-apple-darwin*) ac_webrtc_instset=neon ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" ;; @@ -10730,11 +10772,23 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu ;; *win32* | *w32* | *darwin* | *linux*) case $target in - armv7l*gnueabihf) - ac_webrtc_aec3_instset=neon - ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" + arm*gnueabihf) + if test "x$ax_cv_support_neon_ext" = "xyes"; then + ac_webrtc_aec3_instset=neon + ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" + else + ac_webrtc_aec3_instset=generic + fi + ;; + aarch64*) + if test "x$ax_cv_support_neon_ext" = "xyes"; then + ac_webrtc_aec3_instset=neon + ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" + else + ac_webrtc_aec3_instset=generic + fi ;; - arm-apple-darwin* | aarch64*) + arm-apple-darwin*) ac_webrtc_aec3_instset=neon ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" ;; diff --git a/aconfigure.ac b/aconfigure.ac index 7fd65aac20..92dcaf0140 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -2646,6 +2646,23 @@ AC_ARG_ENABLE(libyuv, AC_MSG_RESULT([Checking if libyuv is disabled...no]) ) +dnl proper neon detector +SAVED_CFLAGS="$CFLAGS" +case $target_cpu in + arm*) + CFLAGS="-mfpu=neon $CFLAGS" + ;; + aarch64*) + CFLAGS="-march=armv8-a+simd $CFLAGS" + ;; +esac +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM()], + [ax_cv_support_neon_ext=yes], + [ax_cv_support_neon_ext=no] +) +CFLAGS="$SAVED_CFLAGS" + dnl # Include webrtc AC_SUBST(ac_no_webrtc) AC_SUBST(ac_webrtc_instset) @@ -2711,11 +2728,23 @@ AC_ARG_ENABLE(libwebrtc, ;; *win32* | *w32* | *darwin* | *linux*) case $target in - armv7l*gnueabihf) - ac_webrtc_instset=neon - ac_webrtc_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" + arm*gnueabihf) + if test "x$ax_cv_support_neon_ext" = "xyes"; then + ac_webrtc_instset=neon + ac_webrtc_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" + else + ac_webrtc_instset=generic + fi + ;; + aarch64*) + if test "x$ax_cv_support_neon_ext" = "xyes"; then + ac_webrtc_instset=neon + ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" + else + ac_webrtc_instset=generic + fi ;; - arm-apple-darwin* | aarch64*) + arm-apple-darwin*) ac_webrtc_instset=neon ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" ;; @@ -2806,11 +2835,23 @@ AC_ARG_ENABLE(libwebrtc_aec3, ;; *win32* | *w32* | *darwin* | *linux*) case $target in - armv7l*gnueabihf) - ac_webrtc_aec3_instset=neon - ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" + arm*gnueabihf) + if test "x$ax_cv_support_neon_ext" = "xyes"; then + ac_webrtc_aec3_instset=neon + ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM_V7 -mfloat-abi=hard -mfpu=neon" + else + ac_webrtc_aec3_instset=generic + fi + ;; + aarch64*) + if test "x$ax_cv_support_neon_ext" = "xyes"; then + ac_webrtc_aec3_instset=neon + ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" + else + ac_webrtc_aec3_instset=generic + fi ;; - arm-apple-darwin* | aarch64*) + arm-apple-darwin*) ac_webrtc_aec3_instset=neon ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" ;; From cf270ff02c0e2a7ac73f909cb96948d9e807218e Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 14 Nov 2024 10:57:45 +0300 Subject: [PATCH 105/491] Fix pkg-config invocation during cross-compilation (#4153) --- aconfigure | 152 +++++++++++++++++--------------------------------- aconfigure.ac | 11 +--- 2 files changed, 54 insertions(+), 109 deletions(-) diff --git a/aconfigure b/aconfigure index cf6185c869..fc3881d17f 100755 --- a/aconfigure +++ b/aconfigure @@ -690,7 +690,6 @@ ac_openh264_ldflags ac_openh264_cflags ac_v4l2_ldflags ac_v4l2_cflags -PKG_CONFIG SAVED_PKG_CONFIG_PATH ac_ffmpeg_ldflags ac_ffmpeg_cflags @@ -744,6 +743,7 @@ ac_linux_poll ac_os_objs ac_std_cpp_lib ac_target_arch +PKG_CONFIG ac_cross_compile ac_shlib_suffix ac_cflags @@ -5255,6 +5255,54 @@ then : fi +for ac_prog in $host_cpu-$host_os-pkg-config $host-pkg-config pkg-config "python pkgconfig.py" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_PKG_CONFIG+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$PKG_CONFIG"; then + ac_cv_prog_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_PKG_CONFIG="$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +PKG_CONFIG=$ac_cv_prog_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 +printf "%s\n" "$PKG_CONFIG" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + test -n "$PKG_CONFIG" && break +done +test -n "$PKG_CONFIG" || PKG_CONFIG="none" + # Check whether --enable-libuuid was given. if test ${enable_libuuid+y} @@ -8153,7 +8201,7 @@ printf "%s\n" "Checking if SDL is disabled... yes" >&6; } else $as_nop - if test "x$with_sdl" != "xno" -a "x$with_sdl" != "x"; then + if test "x$with_sdl" != "xyes" -a "x$with_sdl" != "xno" -a "x$with_sdl" != "x"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using SDL prefix... $with_sdl" >&5 printf "%s\n" "Using SDL prefix... $with_sdl" >&6; } for ac_prog in sdl2-config sdl-config @@ -8315,62 +8363,13 @@ else $as_nop FFMPEG_PREFIX="" SAVED_PKG_CONFIG_PATH=$PKG_CONFIG_PATH - if test "x$with_ffmpeg" != "xno" -a "x$with_ffmpeg" != "x"; then + if test "x$with_ffmpeg" != "xyes" -a "x$with_ffmpeg" != "xno" -a "x$with_ffmpeg" != "x"; then FFMPEG_PREFIX=$with_ffmpeg { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using ffmpeg prefix... $FFMPEG_PREFIX" >&5 printf "%s\n" "Using ffmpeg prefix... $FFMPEG_PREFIX" >&6; } export PKG_CONFIG_PATH=$FFMPEG_PREFIX/lib/pkgconfig fi - for ac_prog in pkg-config "python pkgconfig.py" -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_PKG_CONFIG+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$PKG_CONFIG"; then - ac_cv_prog_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_PKG_CONFIG="$ac_prog" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -PKG_CONFIG=$ac_cv_prog_PKG_CONFIG -if test -n "$PKG_CONFIG"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 -printf "%s\n" "$PKG_CONFIG" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - test -n "$PKG_CONFIG" && break -done -test -n "$PKG_CONFIG" || PKG_CONFIG="none" - - if test "$PKG_CONFIG" != "none"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking ffmpeg packages" >&5 printf %s "checking ffmpeg packages... " >&6; } @@ -9687,55 +9686,6 @@ printf "%s\n" "** OpenSSL libraries not found **" >&6; } printf "%s\n" "Using GnuTLS prefix... $with_gnutls" >&6; } fi - for ac_prog in $host-pkg-config pkg-config "python pkgconfig.py" -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_PKG_CONFIG+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$PKG_CONFIG"; then - ac_cv_prog_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_PKG_CONFIG="$ac_prog" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -PKG_CONFIG=$ac_cv_prog_PKG_CONFIG -if test -n "$PKG_CONFIG"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 -printf "%s\n" "$PKG_CONFIG" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - test -n "$PKG_CONFIG" && break -done -test -n "$PKG_CONFIG" || PKG_CONFIG="none" - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: checking for GnuTLS installations.." >&5 printf "%s\n" "checking for GnuTLS installations.." >&6; } diff --git a/aconfigure.ac b/aconfigure.ac index 92dcaf0140..6186b1f851 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -115,6 +115,7 @@ AC_CHECK_LIB(winmm,puts) AC_CHECK_LIB(socket,puts) AC_CHECK_LIB(rt,puts) AC_CHECK_LIB(m,sin) +AC_CHECK_PROGS(PKG_CONFIG,$host_cpu-$host_os-pkg-config $host-pkg-config pkg-config "python pkgconfig.py",none) dnl dnl libuuid @@ -1528,7 +1529,7 @@ AC_ARG_ENABLE(sdl, fi ], [ - if test "x$with_sdl" != "xno" -a "x$with_sdl" != "x"; then + if test "x$with_sdl" != "xyes" -a "x$with_sdl" != "xno" -a "x$with_sdl" != "x"; then AC_MSG_RESULT([Using SDL prefix... $with_sdl]) AC_PATH_PROGS(SDL_CONFIG,sdl2-config sdl-config,,$with_sdl/bin) else @@ -1579,14 +1580,12 @@ AC_ARG_ENABLE(ffmpeg, FFMPEG_PREFIX="" AC_SUBST(SAVED_PKG_CONFIG_PATH) SAVED_PKG_CONFIG_PATH=$PKG_CONFIG_PATH - if test "x$with_ffmpeg" != "xno" -a "x$with_ffmpeg" != "x"; then + if test "x$with_ffmpeg" != "xyes" -a "x$with_ffmpeg" != "xno" -a "x$with_ffmpeg" != "x"; then FFMPEG_PREFIX=$with_ffmpeg AC_MSG_RESULT([Using ffmpeg prefix... $FFMPEG_PREFIX]) export PKG_CONFIG_PATH=$FFMPEG_PREFIX/lib/pkgconfig fi - AC_CHECK_PROGS(PKG_CONFIG,pkg-config "python pkgconfig.py",none) - if test "$PKG_CONFIG" != "none"; then AC_MSG_CHECKING([ffmpeg packages]) av_pkg="" @@ -2240,10 +2239,6 @@ AC_ARG_ENABLE(ssl, AC_MSG_RESULT([Using GnuTLS prefix... $with_gnutls]) fi - AC_CHECK_PROGS(PKG_CONFIG, - $host-pkg-config pkg-config "python pkgconfig.py", - none) - AC_MSG_RESULT([checking for GnuTLS installations..]) AC_SUBST(gnutls_h_present) AC_SUBST(libgnutls_present) From f5d0bd0d164c79a6b6f4e07d7edc4c23e90f162a Mon Sep 17 00:00:00 2001 From: sauwming Date: Thu, 14 Nov 2024 16:26:40 +0800 Subject: [PATCH 106/491] Revert "Fixed doc for sending DTMF (#4099)" (#4160) This reverts commit e289ddf3d80c61ff5ec37ce4ca49a24997103a16. --- pjsip/include/pjsua-lib/pjsua.h | 3 +-- pjsip/include/pjsua2/call.hpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 6014c957d4..89ebad68ff 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -5623,8 +5623,7 @@ typedef struct pjsua_call_send_dtmf_param pjsua_dtmf_method method; /** - * The signal duration used for the DTMF. This field is only used - * if the method is PJSUA_DTMF_METHOD_SIP_INFO. + * The signal duration used for the DTMF. * * Default: PJSUA_CALL_SEND_DTMF_DURATION_DEFAULT */ diff --git a/pjsip/include/pjsua2/call.hpp b/pjsip/include/pjsua2/call.hpp index d37df81b02..743da677ac 100644 --- a/pjsip/include/pjsua2/call.hpp +++ b/pjsip/include/pjsua2/call.hpp @@ -1355,8 +1355,7 @@ struct CallSendDtmfParam pjsua_dtmf_method method; /** - * The signal duration used for the DTMF. This field is only used - * if the method is PJSUA_DTMF_METHOD_SIP_INFO. + * The signal duration used for the DTMF. * * Default: PJSUA_CALL_SEND_DTMF_DURATION_DEFAULT */ From 4aa26a9e7474794ac5376a6b45e3a4592fecb466 Mon Sep 17 00:00:00 2001 From: jimying Date: Fri, 15 Nov 2024 11:26:46 +0800 Subject: [PATCH 107/491] Fix a log print error (#4161) --- pjlib/src/pj/os_core_win32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c index 4eb8c00fd1..4941027bdf 100644 --- a/pjlib/src/pj/os_core_win32.c +++ b/pjlib/src/pj/os_core_win32.c @@ -54,7 +54,7 @@ #else # define LOG_MUTEX(expr) PJ_LOG(6,expr) #endif -# define LOG_MUTEX_WARN(expr) PJ_LOG(3,expr) +# define LOG_MUTEX_WARN(expr) PJ_PERROR(3,expr) #define THIS_FILE "os_core_win32.c" /* From 77754dd9fe321110f497063855b6fc453054c17d Mon Sep 17 00:00:00 2001 From: "R. Jeske" Date: Mon, 18 Nov 2024 08:01:26 +0100 Subject: [PATCH 108/491] fix timestamp and duration for received dtmf events for G722 and Opus (#4117) (#4159) --- pjmedia/src/pjmedia/stream.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index d76df23523..570404cfc9 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -1771,6 +1771,7 @@ static void handle_incoming_dtmf( pjmedia_stream *stream, pjmedia_stream_dtmf_event dtmf_event; pj_bool_t is_event_end; pj_bool_t emit_event; + float ts_modifier = 1.0; /* Check compiler packing. */ pj_assert(sizeof(pjmedia_rtp_dtmf_event)==4); @@ -1797,6 +1798,18 @@ static void handle_incoming_dtmf( pjmedia_stream *stream, event_duration = pj_ntohs(event->duration); is_event_end = (event->e_vol & PJMEDIA_RTP_DTMF_EVENT_END_MASK) != 0; + /* Correct duration and timestamp for codecs where clock_rate + * differs from sample rate + */ +#if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0) + if (stream->has_g722_mpeg_bug == PJ_TRUE) { + ts_modifier = 2; + } +#endif + if (!pj_stricmp2(&stream->si.fmt.encoding_name, "opus")) { + ts_modifier = (float)stream->codec_param.info.clock_rate / 48000; + } + /* Check if this is the same/current digit of the last packet. */ if (stream->last_dtmf != -1 && event->event == stream->last_dtmf && @@ -1814,9 +1827,9 @@ static void handle_incoming_dtmf( pjmedia_stream *stream, */ if (stream->dtmf_event_cb && emit_event) { dtmf_event.digit = digitmap[event->event]; - dtmf_event.timestamp = (pj_uint32_t)(timestamp->u64 / + dtmf_event.timestamp = (pj_uint32_t)(ts_modifier * timestamp->u64 / (stream->codec_param.info.clock_rate / 1000)); - dtmf_event.duration = (pj_uint16_t)(event_duration / + dtmf_event.duration = (pj_uint16_t)(ts_modifier * event_duration / (stream->codec_param.info.clock_rate / 1000)); dtmf_event.flags = PJMEDIA_STREAM_DTMF_IS_UPDATE; if (is_event_end) { @@ -1842,9 +1855,9 @@ static void handle_incoming_dtmf( pjmedia_stream *stream, */ if (stream->dtmf_event_cb) { dtmf_event.digit = digitmap[event->event]; - dtmf_event.timestamp = (pj_uint32_t)(timestamp->u64 / + dtmf_event.timestamp = (pj_uint32_t)(ts_modifier * timestamp->u64 / (stream->codec_param.info.clock_rate / 1000)); - dtmf_event.duration = (pj_uint16_t)(event_duration / + dtmf_event.duration = (pj_uint16_t)(ts_modifier * event_duration / (stream->codec_param.info.clock_rate / 1000)); dtmf_event.flags = 0; if (is_event_end) { From 71a6c3a115537c2dc6749c1b4b6f12328b7d257f Mon Sep 17 00:00:00 2001 From: jimying Date: Mon, 18 Nov 2024 16:35:31 +0800 Subject: [PATCH 109/491] Fix set/get locale for error string never work in mingw (#4162) in mingw, autoconf set PJ_WIN32_WINNT/WINVER to 0x0400, but SetThreadLocale/GetThreadLocale need WINVER >= 0x0500 --- aconfigure | 2 +- aconfigure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aconfigure b/aconfigure index fc3881d17f..d5d587bfcd 100755 --- a/aconfigure +++ b/aconfigure @@ -5770,7 +5770,7 @@ case $target in *mingw* | *cygw* | *win32* | *w32* ) printf "%s\n" "#define PJ_WIN32 1" >>confdefs.h - printf "%s\n" "#define PJ_WIN32_WINNT 0x0400" >>confdefs.h + printf "%s\n" "#define PJ_WIN32_WINNT 0x0501" >>confdefs.h printf "%s\n" "#define WIN32_LEAN_AND_MEAN 1" >>confdefs.h diff --git a/aconfigure.ac b/aconfigure.ac index 6186b1f851..9095dbf2ce 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -181,7 +181,7 @@ case $target in ;; *mingw* | *cygw* | *win32* | *w32* ) AC_DEFINE(PJ_WIN32,1) - AC_DEFINE(PJ_WIN32_WINNT,0x0400) + AC_DEFINE(PJ_WIN32_WINNT,0x0501) AC_DEFINE(WIN32_LEAN_AND_MEAN) case $target in *_64-w64-mingw* ) From 56854864678bcaef2b358d02f42fddb2d2fe6b7f Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 20 Nov 2024 16:08:08 +0800 Subject: [PATCH 110/491] Revert Swig Python cc_mingw modification (#4165) --- pjsip-apps/src/swig/python/cc_mingw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pjsip-apps/src/swig/python/cc_mingw.c b/pjsip-apps/src/swig/python/cc_mingw.c index c25d47a863..8beef4da9f 100644 --- a/pjsip-apps/src/swig/python/cc_mingw.c +++ b/pjsip-apps/src/swig/python/cc_mingw.c @@ -41,7 +41,7 @@ const char* find_gcc(const char *gcc_exe) return NULL; } - pj_ansi_strxcpy(spath, p, sizeof(spath)); + strncpy(spath, p, sizeof(spath)); p = strtok(spath, ";"); while (p) { int len; From e111ef6503f25699a5ade407e9fc92b01496e77d Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Wed, 20 Nov 2024 15:30:00 +0700 Subject: [PATCH 111/491] Fix tonegen releases pool in wrong function. (#4166) --- pjmedia/src/pjmedia/tonegen.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pjmedia/src/pjmedia/tonegen.c b/pjmedia/src/pjmedia/tonegen.c index 9cd26d390d..d88a9a2b88 100644 --- a/pjmedia/src/pjmedia/tonegen.c +++ b/pjmedia/src/pjmedia/tonegen.c @@ -572,10 +572,16 @@ static pj_status_t tonegen_destroy(pjmedia_port *port) TRACE_((THIS_FILE, "tonegen_destroy()")); - pj_lock_acquire(tonegen->lock); - pj_lock_release(tonegen->lock); + if (tonegen->lock) { + pj_lock_acquire(tonegen->lock); + pj_lock_release(tonegen->lock); - pj_lock_destroy(tonegen->lock); + pj_lock_destroy(tonegen->lock); + tonegen->lock = NULL; + } + + if (tonegen->pool) + pj_pool_safe_release(&tonegen->pool); return PJ_SUCCESS; } @@ -926,9 +932,6 @@ PJ_DEF(pj_status_t) pjmedia_tonegen_set_digit_map(pjmedia_port *port, pj_lock_release(tonegen->lock); - if (tonegen->pool) - pj_pool_safe_release(&tonegen->pool); - return PJ_SUCCESS; } From ee6be4ba50ad6310425d617d50f24334d449c6c5 Mon Sep 17 00:00:00 2001 From: Florian Xaver <29056908+wosrediinanatour@users.noreply.github.com> Date: Thu, 21 Nov 2024 04:38:52 +0100 Subject: [PATCH 112/491] Add support of out-of-dialog SIP transactions (#4155) --- pjsip/include/pjsua-lib/pjsua.h | 37 ++++++++++++++++ pjsip/include/pjsua2/account.hpp | 70 +++++++++++++++++++++++++++++++ pjsip/include/pjsua2/endpoint.hpp | 3 ++ pjsip/src/pjsua-lib/pjsua_acc.c | 70 +++++++++++++++++++++++++++++++ pjsip/src/pjsua2/account.cpp | 17 ++++++++ pjsip/src/pjsua2/endpoint.cpp | 37 +++++++++++----- 6 files changed, 224 insertions(+), 10 deletions(-) diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 89ebad68ff..b64d653f2e 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -1211,6 +1211,19 @@ typedef struct pjsua_callback pjsip_transaction *tsx, pjsip_event *e); + /** + * Notify application when a transaction started by pjsua_acc_send_request() + * has been completed,i.e. when a response has been received. + * + * @param acc_id Account identification. + * @param token Arbitrary data owned by the application + * that was specified when sending the request. + * @param event Transaction event that caused the state change. + */ + void (*on_acc_send_request)(pjsua_acc_id acc_id, + void *token, + pjsip_event *event); + /** * Notify application when media state in the call has changed. * Normal application would need to implement this callback, e.g. @@ -4939,6 +4952,30 @@ PJ_DECL(pj_status_t) pjsua_acc_get_config(pjsua_acc_id acc_id, PJ_DECL(pj_status_t) pjsua_acc_modify(pjsua_acc_id acc_id, const pjsua_acc_config *acc_cfg); +/** + * Send arbitrary out-of-dialog requests from an account, e.g. OPTIONS. + * The application should use the call or presence API to create + * dialog-related requests. + * + * @param acc_id The account ID. + * @param dest_uri URI to be put into the To header (normally is the same + * as the target URI). + * @param method The SIP method of the request. + * @param options This is for future use (currently only NULL is supported). + * @param token Arbitrary token (user data owned by the application) + * to be passed back to the application in callback + * on_acc_send_request(). + * @param msg_data Optional headers etc. to be added to the outgoing + * request, or NULL if no custom header is desired. + * + * @return PJ_SUCCESS or the error code. + */ +PJ_DECL(pj_status_t) pjsua_acc_send_request(pjsua_acc_id acc_id, + const pj_str_t *dest_uri, + const pj_str_t *method, + void *options, + void *token, + const pjsua_msg_data *msg_data); /** * Modify account's presence status to be advertised to remote/presence diff --git a/pjsip/include/pjsua2/account.hpp b/pjsip/include/pjsua2/account.hpp index a39f1b634b..ad7d4472d4 100644 --- a/pjsip/include/pjsua2/account.hpp +++ b/pjsip/include/pjsua2/account.hpp @@ -799,6 +799,37 @@ struct AccountNatConfig : public PersistentObject virtual void writeObject(ContainerNode &node) const PJSUA2_THROW(Error); }; +/** + * This structure contains parameters for Account::sendRequest() + */ +struct SendRequestParam +{ + /** + * Token or arbitrary user data ownd by the application, + * which will be passed back in callback Account::onSendRequest(). + */ + Token userData; + + /** + * SIP method of the request. + */ + string method; + + /** + * Message body and/or list of headers etc. to be included in + * the outgoing request. + */ + SipTxOption txOption; + +public: + /** + * Default constructor initializes with zero/empty values. + */ + SendRequestParam(); +}; + + + /** * SRTP crypto. */ @@ -1725,6 +1756,24 @@ struct OnMwiInfoParam SipRxData rdata; }; +/** + * This structure contains parameters for Account::onSendRequest() callback. + */ +struct OnSendRequestParam +{ + /** + * Token or arbitrary user data owned by the application, + * which was passed to Endpoint::sendRquest() function. + */ + Token userData; + + /** + * Transaction event that caused the state change. + */ + SipEvent e; +}; + + /** * Parameters for presNotify() account method. */ @@ -1901,6 +1950,18 @@ class Account */ AccountInfo getInfo() const PJSUA2_THROW(Error); + /** + * Send arbitrary requests using the account. Application should only use + * this function to create auxiliary requests outside dialog, such as + * OPTIONS, and use the call or presence API to create dialog related + * requests. + * + * @param prm.method SIP method of the request. + * @param prm.txOption Optional message body and/or list of headers to be + * included in outgoing request. + */ + void sendRequest(const pj::SendRequestParam& prm) PJSUA2_THROW(Error); + /** * Update registration or perform unregistration. Application normally * only needs to call this function if it wants to manually update the @@ -2082,6 +2143,15 @@ class Account virtual void onInstantMessageStatus(OnInstantMessageStatusParam &prm) { PJ_UNUSED_ARG(prm); } + /** + * Notify application when a transaction started by Account::sendRequest() + * has been completed,i.e. when a response has been received. + * + * @param prm Callback parameter. + */ + virtual void onSendRequest(OnSendRequestParam &prm) + { PJ_UNUSED_ARG(prm); } + /** * Notify application about typing indication. * diff --git a/pjsip/include/pjsua2/endpoint.hpp b/pjsip/include/pjsua2/endpoint.hpp index 774edb759d..cb78cc3b6c 100644 --- a/pjsip/include/pjsua2/endpoint.hpp +++ b/pjsip/include/pjsua2/endpoint.hpp @@ -2041,6 +2041,9 @@ class Endpoint pj_bool_t renew); static void on_reg_state2(pjsua_acc_id acc_id, pjsua_reg_info *info); + static void on_acc_send_request(pjsua_acc_id acc_id, + void* token, + pjsip_event *event); static void on_incoming_subscribe(pjsua_acc_id acc_id, pjsua_srv_pres *srv_pres, pjsua_buddy_id buddy_id, diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index a46336f2e1..c66c54faff 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -1510,6 +1510,76 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id, return status; } +typedef struct send_request_data +{ + pjsua_acc_id acc_id; + void *token; +} send_request_data; + +static void on_send_request(void *request_data, pjsip_event *event) +{ + send_request_data *data = (send_request_data*) request_data; + if (data && pjsua_var.ua_cfg.cb.on_acc_send_request) + (pjsua_var.ua_cfg.cb.on_acc_send_request)(data->acc_id, data->token, event); +} + +PJ_DEF(pj_status_t) pjsua_acc_send_request(pjsua_acc_id acc_id, + const pj_str_t *dest_uri, + const pj_str_t *method, + void *options, + void *token, + const pjsua_msg_data *msg_data) +{ + const pjsip_hdr *cap_hdr = NULL; + pjsip_method method_; + pj_status_t status; + pjsip_tx_data *tdata = NULL; + send_request_data *request_data = NULL; + + PJ_ASSERT_RETURN(acc_id>=0, PJ_EINVAL); + PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL); + PJ_ASSERT_RETURN(method, PJ_EINVAL); + PJ_UNUSED_ARG(options); + PJ_ASSERT_RETURN(msg_data, PJ_EINVAL); + + PJ_LOG(4,(THIS_FILE, "Account %d sending %.*s request..", + acc_id, (int)method->slen, method->ptr)); + pj_log_push_indent(); + + pjsip_method_init_np(&method_, (pj_str_t*)method); + status = pjsua_acc_create_request(acc_id, &method_, &msg_data->target_uri, &tdata); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to create request", status); + goto on_return; + } + + request_data = PJ_POOL_ZALLOC_T(tdata->pool, send_request_data); + if (!request_data) { + status = PJ_ENOMEM; + goto on_return; + } + request_data->acc_id = acc_id; + request_data->token = token; + + pjsua_process_msg_data(tdata, msg_data); + + cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, NULL); + if (cap_hdr) { + pjsip_msg_add_hdr(tdata->msg, + (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr)); + } + + status = pjsip_endpt_send_request(pjsua_var.endpt, tdata, -1, request_data, &on_send_request); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to send request", status); + goto on_return; + } + +on_return: + pj_log_pop_indent(); + return status; +} + /* * Modify account's presence status to be advertised to remote/presence diff --git a/pjsip/src/pjsua2/account.cpp b/pjsip/src/pjsua2/account.cpp index 2558596fdd..7c65bfc038 100644 --- a/pjsip/src/pjsua2/account.cpp +++ b/pjsip/src/pjsua2/account.cpp @@ -117,6 +117,13 @@ void RtcpFbConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error) /////////////////////////////////////////////////////////////////////////////// +SendRequestParam::SendRequestParam() +: userData(NULL), method("") +{ +} + +/////////////////////////////////////////////////////////////////////////////// + void SrtpCrypto::fromPj(const pjmedia_srtp_crypto &prm) { this->key = pj2Str(prm.key); @@ -1063,6 +1070,16 @@ AccountInfo Account::getInfo() const PJSUA2_THROW(Error) return ai; } +void Account::sendRequest(const pj::SendRequestParam& prm) PJSUA2_THROW(Error) +{ + pj_str_t method = str2Pj(prm.method); + pj_str_t dest_uri = str2Pj(prm.txOption.targetUri); + pjsua_msg_data msg_data; + prm.txOption.toPj(msg_data); + + PJSUA2_CHECK_EXPR(pjsua_acc_send_request(id, &dest_uri, &method, NULL, prm.userData, &msg_data)); +} + void Account::setRegistration(bool renew) PJSUA2_THROW(Error) { PJSUA2_CHECK_EXPR( pjsua_acc_set_registration(id, renew) ); diff --git a/pjsip/src/pjsua2/endpoint.cpp b/pjsip/src/pjsua2/endpoint.cpp index 610ec8285b..3a2b381ef7 100644 --- a/pjsip/src/pjsua2/endpoint.cpp +++ b/pjsip/src/pjsua2/endpoint.cpp @@ -924,6 +924,22 @@ void Endpoint::on_reg_state2(pjsua_acc_id acc_id, pjsua_reg_info *info) acc->onRegState(prm); } +void Endpoint::on_acc_send_request(pjsua_acc_id acc_id, + void *token, + pjsip_event *event) +{ + Account *acc = lookupAcc(acc_id, "on_acc_send_request:response()"); + if (!acc) { + return; + } + + OnSendRequestParam prm; + prm.userData = token; + prm.e.fromPj(*event); + + acc->onSendRequest(prm); +} + void Endpoint::on_incoming_subscribe(pjsua_acc_id acc_id, pjsua_srv_pres *srv_pres, pjsua_buddy_id buddy_id, @@ -1930,16 +1946,17 @@ void Endpoint::libInit(const EpConfig &prmEpConfig) PJSUA2_THROW(Error) ua_cfg.cb.on_nat_detect = &Endpoint::on_nat_detect; ua_cfg.cb.on_transport_state = &Endpoint::on_transport_state; - ua_cfg.cb.on_incoming_call = &Endpoint::on_incoming_call; - ua_cfg.cb.on_reg_started = &Endpoint::on_reg_started; - ua_cfg.cb.on_reg_state2 = &Endpoint::on_reg_state2; - ua_cfg.cb.on_incoming_subscribe = &Endpoint::on_incoming_subscribe; - ua_cfg.cb.on_pager2 = &Endpoint::on_pager2; - ua_cfg.cb.on_pager_status2 = &Endpoint::on_pager_status2; - ua_cfg.cb.on_typing2 = &Endpoint::on_typing2; - ua_cfg.cb.on_mwi_info = &Endpoint::on_mwi_info; - ua_cfg.cb.on_buddy_state = &Endpoint::on_buddy_state; - ua_cfg.cb.on_buddy_evsub_state = &Endpoint::on_buddy_evsub_state; + ua_cfg.cb.on_acc_send_request = &Endpoint::on_acc_send_request; + ua_cfg.cb.on_incoming_call = &Endpoint::on_incoming_call; + ua_cfg.cb.on_reg_started = &Endpoint::on_reg_started; + ua_cfg.cb.on_reg_state2 = &Endpoint::on_reg_state2; + ua_cfg.cb.on_incoming_subscribe = &Endpoint::on_incoming_subscribe; + ua_cfg.cb.on_pager2 = &Endpoint::on_pager2; + ua_cfg.cb.on_pager_status2 = &Endpoint::on_pager_status2; + ua_cfg.cb.on_typing2 = &Endpoint::on_typing2; + ua_cfg.cb.on_mwi_info = &Endpoint::on_mwi_info; + ua_cfg.cb.on_buddy_state = &Endpoint::on_buddy_state; + ua_cfg.cb.on_buddy_evsub_state = &Endpoint::on_buddy_evsub_state; ua_cfg.cb.on_acc_find_for_incoming = &Endpoint::on_acc_find_for_incoming; ua_cfg.cb.on_ip_change_progress = &Endpoint::on_ip_change_progress; From 57acd3b1306b653fc185a73c3d659300ebe6c4f5 Mon Sep 17 00:00:00 2001 From: Riza Sulistyo Date: Thu, 21 Nov 2024 11:20:09 +0700 Subject: [PATCH 113/491] Reset regc info transport (#4168) * Reset regc->info_transport on pjsip_regc_release_transport() * change the check --- pjsip/src/pjsip-ua/sip_reg.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c index 25cf6589e6..ed9ba6d451 100644 --- a/pjsip/src/pjsip-ua/sip_reg.c +++ b/pjsip/src/pjsip-ua/sip_reg.c @@ -490,6 +490,9 @@ PJ_DEF(pj_status_t) pjsip_regc_release_transport(pjsip_regc *regc) pjsip_transport_dec_ref(regc->last_transport); regc->last_transport = NULL; } + if (regc->info_transport) { + regc->info_transport = NULL; + } return PJ_SUCCESS; } From 2480d425ecd8ecdde577ac9e390cf25224768ebe Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 21 Nov 2024 16:01:00 +0700 Subject: [PATCH 114/491] Apply stricter rule for pool name with percent char ('%p'). (#4169) --- pjlib/src/pj/pool.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pjlib/src/pj/pool.c b/pjlib/src/pj/pool.c index 0f7910a0ca..05d15aebb9 100644 --- a/pjlib/src/pj/pool.c +++ b/pjlib/src/pj/pool.c @@ -177,7 +177,9 @@ PJ_DEF(void) pj_pool_init_int( pj_pool_t *pool, pool->callback = callback; if (name) { - if (strchr(name, '%') != NULL) { + char *p = pj_ansi_strchr(name, '%'); + if (p && *(p+1)=='p' && *(p+2)=='\0') { + /* Special name with "%p" suffix */ pj_ansi_snprintf(pool->obj_name, sizeof(pool->obj_name), name, pool); } else { From e6eb88eefe1ec4210c341cea4a7e94ba0da21100 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 21 Nov 2024 16:03:05 +0700 Subject: [PATCH 115/491] Fix audio & video conference bridge: adding port may not update port counter (#4164) * Fix audio & video conference bridges, adding port may not update port counter. * Make sure pool name is NULL terminated, don't use remote URI as conf port name when it contains percent char --- pjmedia/src/pjmedia/conference.c | 74 ++++++++++++++++++++++--------- pjmedia/src/pjmedia/vid_conf.c | 76 +++++++++++++++++++++++--------- pjsip/src/pjsua-lib/pjsua_aud.c | 6 ++- 3 files changed, 112 insertions(+), 44 deletions(-) diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c index ab525b4add..924100bda8 100644 --- a/pjmedia/src/pjmedia/conference.c +++ b/pjmedia/src/pjmedia/conference.c @@ -281,6 +281,7 @@ static pj_status_t destroy_port_pasv(pjmedia_port *this_port); typedef enum op_type { OP_UNKNOWN, + OP_ADD_PORT, OP_REMOVE_PORT, OP_CONNECT_PORTS, OP_DISCONNECT_PORTS, @@ -289,6 +290,10 @@ typedef enum op_type /* Synchronized operation parameter. */ typedef union op_param { + struct { + unsigned port; + } add_port; + struct { unsigned port; } remove_port; @@ -314,6 +319,7 @@ typedef struct op_entry { } op_entry; /* Prototypes of synchronized operation */ +static void op_add_port(pjmedia_conf *conf, const op_param *prm); static void op_remove_port(pjmedia_conf *conf, const op_param *prm); static void op_connect_ports(pjmedia_conf *conf, const op_param *prm); static void op_disconnect_ports(pjmedia_conf *conf, const op_param *prm); @@ -342,6 +348,9 @@ static void handle_op_queue(pjmedia_conf *conf) pj_list_erase(op); switch(op->type) { + case OP_ADD_PORT: + op_add_port(conf, &op->param); + break; case OP_REMOVE_PORT: op_remove_port(conf, &op->param); break; @@ -383,10 +392,15 @@ static pj_status_t create_conf_port( pj_pool_t *parent_pool, { struct conf_port *conf_port; pj_pool_t *pool = NULL; + char pname[PJ_MAX_OBJ_NAME]; pj_status_t status = PJ_SUCCESS; + /* Make sure pool name is NULL terminated */ + pj_assert(name); + pj_ansi_strxcpy2(pname, name, sizeof(pname)); + /* Create own pool */ - pool = pj_pool_create(parent_pool->factory, name->ptr, 500, 500, NULL); + pool = pj_pool_create(parent_pool->factory, pname, 500, 500, NULL); if (!pool) { status = PJ_ENOMEM; goto on_return; @@ -940,6 +954,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf, { struct conf_port *conf_port; unsigned index; + op_entry *ope; pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(conf && pool && strm_port, PJ_EINVAL); @@ -992,14 +1007,24 @@ PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf, conf->ports[index] = conf_port; //conf->port_cnt++; + /* Queue the operation */ + ope = get_free_op_entry(conf); + if (ope) { + ope->type = OP_ADD_PORT; + ope->param.add_port.port = index; + pj_list_push_back(conf->op_queue, ope); + PJ_LOG(4,(THIS_FILE, "Add port %d (%.*s) queued", + index, (int)port_name->slen, port_name->ptr)); + } else { + status = PJ_ENOMEM; + goto on_return; + } + /* Done. */ if (p_port) { *p_port = index; } - PJ_LOG(5,(THIS_FILE, "Adding new port %d (%.*s)", - index, (int)port_name->slen, port_name->ptr)); - on_return: pj_mutex_unlock(conf->mutex); pj_log_pop_indent(); @@ -1008,6 +1033,24 @@ PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf, } +static void op_add_port(pjmedia_conf *conf, const op_param *prm) +{ + unsigned port = prm->add_port.port; + struct conf_port *cport = conf->ports[port]; + + /* Port must be valid and flagged as new. */ + if (!cport || !cport->is_new) + return; + + /* Activate newly added port */ + cport->is_new = PJ_FALSE; + ++conf->port_cnt; + + PJ_LOG(4,(THIS_FILE, "Added port %d (%.*s), port count=%d", + port, (int)cport->name.slen, cport->name.ptr, conf->port_cnt)); +} + + #if !DEPRECATED_FOR_TICKET_2234 /* * Add passive port. @@ -1674,10 +1717,12 @@ static void op_remove_port(pjmedia_conf *conf, const op_param *prm) /* Remove the port. */ conf->ports[port] = NULL; - --conf->port_cnt; + if (!conf_port->is_new) + --conf->port_cnt; - PJ_LOG(4,(THIS_FILE,"Removed port %d (%.*s)", - port, (int)conf_port->name.slen, conf_port->name.ptr)); + PJ_LOG(4,(THIS_FILE,"Removed port %d (%.*s), port count=%d", + port, (int)conf_port->name.slen, conf_port->name.ptr, + conf->port_cnt)); /* Decrease conf port ref count */ if (conf_port->port && conf_port->port->grp_lock) @@ -2368,22 +2413,7 @@ static pj_status_t get_frame(pjmedia_port *this_port, if (!pj_list_empty(conf->op_queue)) { pj_log_push_indent(); pj_mutex_lock(conf->mutex); - - /* Activate any newly added port */ - for (i=0; imax_ports; ++i) { - struct conf_port *port = conf->ports[i]; - if (!port || !port->is_new) - continue; - - port->is_new = PJ_FALSE; - ++conf->port_cnt; - - PJ_LOG(5,(THIS_FILE, "New port %d (%.*s) is added", - i, (int)port->name.slen, port->name.ptr)); - } - handle_op_queue(conf); - pj_mutex_unlock(conf->mutex); pj_log_pop_indent(); } diff --git a/pjmedia/src/pjmedia/vid_conf.c b/pjmedia/src/pjmedia/vid_conf.c index 4eed3463c9..9634c39267 100644 --- a/pjmedia/src/pjmedia/vid_conf.c +++ b/pjmedia/src/pjmedia/vid_conf.c @@ -138,6 +138,7 @@ static void cleanup_render_state(vconf_port *cp, typedef enum op_type { OP_UNKNOWN, + OP_ADD_PORT, OP_REMOVE_PORT, OP_CONNECT_PORTS, OP_DISCONNECT_PORTS, @@ -147,6 +148,10 @@ typedef enum op_type /* Synchronized operation parameter. */ typedef union op_param { + struct { + unsigned port; + } add_port; + struct { unsigned port; } remove_port; @@ -175,6 +180,7 @@ typedef struct op_entry { } op_entry; /* Prototypes of synchronized operation */ +static void op_add_port(pjmedia_vid_conf *vid_conf, const op_param *prm); static void op_remove_port(pjmedia_vid_conf *conf, const op_param *prm); static void op_connect_ports(pjmedia_vid_conf *conf, const op_param *prm); static void op_disconnect_ports(pjmedia_vid_conf *conf, const op_param *prm); @@ -204,6 +210,9 @@ static void handle_op_queue(pjmedia_vid_conf *conf) pj_list_erase(op); switch(op->type) { + case OP_ADD_PORT: + op_add_port(conf, &op->param); + break; case OP_REMOVE_PORT: op_remove_port(conf, &op->param); break; @@ -385,9 +394,11 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_add_port( pjmedia_vid_conf *vid_conf, pj_pool_t *pool = NULL; vconf_port *cport = NULL; unsigned index; + op_entry *ope; + char pname[PJ_MAX_OBJ_NAME]; pj_status_t status = PJ_SUCCESS; - PJ_LOG(5,(THIS_FILE, "Add port %s requested", port->info.name.ptr)); + PJ_LOG(5,(THIS_FILE, "Add video port %s requested", port->info.name.ptr)); PJ_ASSERT_RETURN(vid_conf && parent_pool && port, PJ_EINVAL); PJ_ASSERT_RETURN(port->info.fmt.type==PJMEDIA_TYPE_VIDEO && @@ -407,13 +418,17 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_add_port( pjmedia_vid_conf *vid_conf, break; } if (index == vid_conf->opt.max_slot_cnt) { - PJ_PERROR(3,(THIS_FILE, PJ_ETOOMANY, "Add port %s failed", name->ptr)); + PJ_PERROR(3,(THIS_FILE, PJ_ETOOMANY, "Add video port %s failed", + name->ptr)); pj_mutex_unlock(vid_conf->mutex); return PJ_ETOOMANY; } + /* Make sure pool name is NULL terminated */ + pj_ansi_strxcpy2(pname, name, sizeof(pname)); + /* Create pool */ - pool = pj_pool_create(parent_pool->factory, name->ptr, 500, 500, NULL); + pool = pj_pool_create(parent_pool->factory, pname, 500, 500, NULL); if (!pool) { status = PJ_ENOMEM; goto on_error; @@ -565,9 +580,20 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_add_port( pjmedia_vid_conf *vid_conf, vid_conf->ports[index] = cport; //vid_conf->port_cnt++; - PJ_LOG(4,(THIS_FILE,"Added port %d (%.*s)", - index, (int)cport->name.slen, cport->name.ptr)); + /* Queue the operation */ + ope = get_free_op_entry(vid_conf); + if (ope) { + ope->type = OP_ADD_PORT; + ope->param.add_port.port = index; + pj_list_push_back(vid_conf->op_queue, ope); + PJ_LOG(4,(THIS_FILE,"Add video port %d (%.*s) queued", + index, (int)cport->name.slen, cport->name.ptr)); + } else { + status = PJ_ENOMEM; + goto on_return; + } +on_return: pj_mutex_unlock(vid_conf->mutex); /* Done. */ @@ -581,13 +607,32 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_add_port( pjmedia_vid_conf *vid_conf, if (pool) pj_pool_release(pool); - PJ_PERROR(3, (THIS_FILE, status, "Add port %s failed", name->ptr)); + PJ_PERROR(3, (THIS_FILE, status, "Add video port %s failed", name->ptr)); pj_mutex_unlock(vid_conf->mutex); return status; } +static void op_add_port(pjmedia_vid_conf *vid_conf, + const op_param *prm) +{ + unsigned slot = prm->add_port.port; + vconf_port *cport = vid_conf->ports[slot]; + + /* Port must be valid and flagged as new. */ + if (!cport || !cport->is_new) + return; + + /* Activate newly added port */ + cport->is_new = PJ_FALSE; + ++vid_conf->port_cnt; + PJ_LOG(4,(THIS_FILE,"Added video port %d (%.*s), port count=%d", + cport->idx, (int)cport->name.slen, cport->name.ptr, + vid_conf->port_cnt)); +} + + /* * Remove a media port from the video conference bridge. */ @@ -664,10 +709,12 @@ static void op_remove_port(pjmedia_vid_conf *vid_conf, /* Remove the port. */ vid_conf->ports[slot] = NULL; - --vid_conf->port_cnt; + if (!cport->is_new) + --vid_conf->port_cnt; - PJ_LOG(4,(THIS_FILE,"Removed port %d (%.*s)", - slot, (int)cport->name.slen, cport->name.ptr)); + PJ_LOG(4,(THIS_FILE,"Removed video port %d (%.*s), port count=%d", + slot, (int)cport->name.slen, cport->name.ptr, + vid_conf->port_cnt)); /* Decrease port ref count */ pjmedia_port_dec_ref(cport->port); @@ -1041,17 +1088,6 @@ static void on_clock_tick(const pj_timestamp *now, void *user_data) if (!pj_list_empty(vid_conf->op_queue)) { pj_mutex_lock(vid_conf->mutex); handle_op_queue(vid_conf); - - /* Activate any newly added port */ - for (i=0; iopt.max_slot_cnt; ++i) { - vconf_port *port = vid_conf->ports[i]; - if (!port || !port->is_new) - continue; - - port->is_new = PJ_FALSE; - ++vid_conf->port_cnt; - } - pj_mutex_unlock(vid_conf->mutex); } diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index 9991ad1053..24003b4f18 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -847,8 +847,10 @@ pj_status_t pjsua_aud_channel_update(pjsua_call_media *call_med, port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, call->inv->dlg->remote.info->uri, tmp, sizeof(tmp)); - if (port_name.slen < 1) { - port_name = pj_str("call"); + if (port_name.slen < 1 || pj_strchr(&port_name, '%')) { + pj_ansi_snprintf(tmp, sizeof(tmp), "call %d:%d", + call->index, strm_idx); + port_name = pj_str(tmp); } status = pjmedia_conf_add_port(pjsua_var.mconf, call->inv->pool, From c239fc89e97f50e9ef83021bdaf83cee95130afc Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 22 Nov 2024 20:15:06 +0800 Subject: [PATCH 116/491] Fixed assertion in ICE when trying to add candidate for disabled component (#4171) --- pjnath/src/pjnath/ice_strans.c | 5 ++++- pjsip-apps/src/samples/icedemo.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c index 99018723fc..370ca6f146 100644 --- a/pjnath/src/pjnath/ice_strans.c +++ b/pjnath/src/pjnath/ice_strans.c @@ -2536,7 +2536,10 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, cand->status = PJ_SUCCESS; /* Add the candidate (for trickle ICE) */ - if (pj_ice_strans_has_sess(ice_st)) { + if (pj_ice_strans_has_sess(ice_st) && + comp->comp_id <= + pj_ice_strans_get_running_comp_cnt(ice_st)) + { status = pj_ice_sess_add_cand( ice_st->ice, comp->comp_id, diff --git a/pjsip-apps/src/samples/icedemo.c b/pjsip-apps/src/samples/icedemo.c index 267de3216e..ed8f010a6e 100644 --- a/pjsip-apps/src/samples/icedemo.c +++ b/pjsip-apps/src/samples/icedemo.c @@ -1217,7 +1217,7 @@ int main(int argc, char *argv[]) switch (c) { case 'c': icedemo.opt.comp_cnt = atoi(pj_optarg); - if (icedemo.opt.comp_cnt < 1 || icedemo.opt.comp_cnt >= PJ_ICE_MAX_COMP) { + if (icedemo.opt.comp_cnt < 1 || icedemo.opt.comp_cnt > PJ_ICE_MAX_COMP) { puts("Invalid component count value"); return 1; } From 1e312bf983aab2c8ae0328eda27da105f1c60289 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Mon, 25 Nov 2024 13:54:04 +0700 Subject: [PATCH 117/491] Misc: fix compile warnings on VS2022 (#4174) --- pjlib-util/src/pjlib-util-test/encryption.c | 2 +- pjlib-util/src/pjlib-util-test/http_client.c | 6 ++- pjlib-util/src/pjlib-util/srv_resolver.c | 2 - pjlib/src/pj/os_core_win32.c | 3 +- pjlib/src/pj/string.c | 8 ++-- pjlib/src/pjlib-test/fifobuf.c | 2 +- pjlib/src/pjlib-test/os.c | 3 ++ pjlib/src/pjlib-test/sock.c | 6 ++- pjlib/src/pjlib-test/string.c | 4 +- pjlib/src/pjlib-test/unittest_test.c | 6 +-- pjmedia/src/pjmedia/sdp.c | 2 +- pjmedia/src/pjmedia/stream.c | 3 +- pjsip/src/pjsip-ua/sip_inv.c | 9 +++-- pjsip/src/pjsip/sip_auth_client.c | 2 +- pjsip/src/pjsip/sip_msg.c | 11 ++++-- pjsip/src/pjsua-lib/pjsua_media.c | 2 + pjsip/src/pjsua-lib/pjsua_pres.c | 2 +- pjsip/src/pjsua2/endpoint.cpp | 10 ++--- pjsip/src/pjsua2/media.cpp | 40 ++++++++++---------- pjsip/src/pjsua2/siptypes.cpp | 2 +- pjsip/src/test/transport_loop_test.c | 3 ++ third_party/webrtc/PJSIP_NOTES | 19 ++++++++++ third_party/webrtc/src/pj_config.h | 9 +++++ third_party/webrtc/src/webrtc/typedefs.h | 2 + 24 files changed, 102 insertions(+), 56 deletions(-) create mode 100644 third_party/webrtc/src/pj_config.h diff --git a/pjlib-util/src/pjlib-util-test/encryption.c b/pjlib-util/src/pjlib-util-test/encryption.c index 1364b7d55f..d131e3f633 100644 --- a/pjlib-util/src/pjlib-util-test/encryption.c +++ b/pjlib-util/src/pjlib-util-test/encryption.c @@ -672,7 +672,7 @@ static void crc32_final(pj_crc32_context *ctx, pj_uint32_t *digest) static void md5_update_wrapper(pj_md5_context *ctx, unsigned char const *buf, pj_size_t len) { - pj_md5_update(ctx, buf, len); + pj_md5_update(ctx, buf, (unsigned)len); } int encryption_benchmark() diff --git a/pjlib-util/src/pjlib-util-test/http_client.c b/pjlib-util/src/pjlib-util-test/http_client.c index ddda5d4a86..88f4a8f419 100644 --- a/pjlib-util/src/pjlib-util-test/http_client.c +++ b/pjlib-util/src/pjlib-util-test/http_client.c @@ -119,7 +119,8 @@ static int server_thread(void *p) pkt_len = pj_ansi_snprintf(pkt, srv->buf_size, "HTTP/1.0 200 OK\r\n"); PJ_ASSERT_ON_FAIL(pkt_len>0, { - PJ_PERROR(2, (THIS_FILE, -pkt_len, "Error creating response")); + PJ_LOG(2, (THIS_FILE, "Error creating response, pkt_len=%ld", + pkt_len)); pj_sock_close(newsock); continue; }) @@ -130,7 +131,8 @@ static int server_thread(void *p) } pkt_len = pj_ansi_strxcat(pkt, "\r\n", srv->buf_size); if (pkt_len < 0) { - PJ_PERROR(2, (THIS_FILE, -pkt_len, "Error creating response")); + PJ_LOG(2, (THIS_FILE, "Error creating response 2, pkt_len=%ld", + pkt_len)); pj_sock_close(newsock); continue; } diff --git a/pjlib-util/src/pjlib-util/srv_resolver.c b/pjlib-util/src/pjlib-util/srv_resolver.c index bd1babc1ed..675fadfc80 100644 --- a/pjlib-util/src/pjlib-util/srv_resolver.c +++ b/pjlib-util/src/pjlib-util/srv_resolver.c @@ -36,7 +36,6 @@ struct common pj_dns_type type; /**< Type of this structure.*/ }; -#pragma pack(1) struct srv_target { struct common common; @@ -55,7 +54,6 @@ struct srv_target unsigned addr_cnt; pj_sockaddr addr[ADDR_MAX_COUNT];/**< Address family and IP.*/ }; -#pragma pack() struct pj_dns_srv_async_query { diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c index 4941027bdf..68a538dcba 100644 --- a/pjlib/src/pj/os_core_win32.c +++ b/pjlib/src/pj/os_core_win32.c @@ -480,7 +480,8 @@ static void set_thread_display_name(const char *name) PJ_LOG(5, (THIS_FILE, "SetThreadDescription:%p, name:%s", fn, name)); if (fn) { wchar_t wname[PJ_MAX_OBJ_NAME]; - pj_ansi_to_unicode(name, pj_ansi_strlen(name), wname, PJ_MAX_OBJ_NAME); + pj_ansi_to_unicode(name, (int)pj_ansi_strlen(name), wname, + PJ_MAX_OBJ_NAME); fn(GetCurrentThread(), wname); return; } diff --git a/pjlib/src/pj/string.c b/pjlib/src/pj/string.c index f7dd93da28..13db76f1de 100644 --- a/pjlib/src/pj/string.c +++ b/pjlib/src/pj/string.c @@ -638,10 +638,10 @@ PJ_DEF(int) pj_ansi_strxcpy(char *dst, const char *src, } if (!*dst && !*src) { - return dst-odst; + return (int)(dst-odst); } else { *dst = '\0'; - return *src? -PJ_ETOOBIG : dst-odst; + return *src? -PJ_ETOOBIG : (int)(dst-odst); } } @@ -665,7 +665,7 @@ PJ_DEF(int) pj_ansi_strxcpy2(char *dst, const pj_str_t *src, *dst = '\0'; if (ssrc==esrc || !*ssrc) { - return dst-odst; + return (int)(dst-odst); } else { return -PJ_ETOOBIG; } @@ -685,7 +685,7 @@ PJ_DEF(int) pj_ansi_strxcat(char *dst, const char *src, pj_size_t dst_size) int rc = pj_ansi_strxcpy(dst+dst_len, src, dst_size-dst_len); if (rc < 0) return rc; - return dst_len + rc; + return (int)dst_len + rc; } else return -PJ_ETOOBIG; } diff --git a/pjlib/src/pjlib-test/fifobuf.c b/pjlib/src/pjlib-test/fifobuf.c index 684d0ecc60..d300452ce7 100644 --- a/pjlib/src/pjlib-test/fifobuf.c +++ b/pjlib/src/pjlib-test/fifobuf.c @@ -93,7 +93,7 @@ static int fifobuf_rolling_test() int i, n; /* Allocate random number of chunks */ - n = N/2 + (pj_rand() % (N/2)) - pj_list_size(&chunks); + n = N/2 + (pj_rand() % (N/2)) - (int)pj_list_size(&chunks); for (i=0; i%ld", sv[0], sv[1]); + len = pj_ansi_snprintf(buf, sizeof(buf), "hello, %ld->%ld", + (long)sv[0], (long)sv[1]); rc = pj_sock_send(sv[0], buf, &len, 0); if (rc != PJ_SUCCESS) goto on_error; @@ -879,7 +880,8 @@ static int do_sockpair_tst(int family, int type, int proto) PJ_LOG(3, ("test", "recv: %.*s", (int)len, buf)); /* sv[1] send text to sv[0] */ - len = pj_ansi_snprintf(buf, sizeof(buf), "hello, %ld->%ld", sv[1], sv[0]); + len = pj_ansi_snprintf(buf, sizeof(buf), "hello, %ld->%ld", + (long)sv[1], (long)sv[0]); rc = pj_sock_send(sv[1], buf, &len, 0); if (rc != PJ_SUCCESS) goto on_error; diff --git a/pjlib/src/pjlib-test/string.c b/pjlib/src/pjlib-test/string.c index 09757fa01a..7dec3ba468 100644 --- a/pjlib/src/pjlib-test/string.c +++ b/pjlib/src/pjlib-test/string.c @@ -325,7 +325,7 @@ static int verify_strxcpy(const char *src, int dst_size, int exp_ret, } /* verify not writing pass buffer */ - for (i=exp_dst?strlen(exp_dst)+1:0; i<(int)sizeof(dst); ++i) { + for (i=exp_dst?(int)strlen(exp_dst)+1:0; i<(int)sizeof(dst); ++i) { if (dst[i] != GUARD) { PJ_LOG(3,("", " strxcpy \"%s\", dst_size=%d: overflow at %d", src, dst_size, i)); @@ -561,7 +561,7 @@ static int verify_strxcat(const char *cdst, const char *src, int dst_size, } /* verify not writing past buffer */ - for (i=exp_dst?strlen(exp_dst)+1:0; i<(int)sizeof(dst); ++i) { + for (i=exp_dst?(int)strlen(exp_dst)+1:0; i<(int)sizeof(dst); ++i) { if (dst[i] != GUARD) { PJ_LOG(3,("", " strxcat \"%s\", \"%s\", dst_size=%d: " "overflow at %d", diff --git a/pjlib/src/pjlib-test/unittest_test.c b/pjlib/src/pjlib-test/unittest_test.c index 65c10a4881..980e1990fc 100644 --- a/pjlib/src/pjlib-test/unittest_test.c +++ b/pjlib/src/pjlib-test/unittest_test.c @@ -44,12 +44,12 @@ static void print_log_buffer(char *title) /* This log callback appends the log to the log_buffer */ static void log_callback(int level, const char *data, int len) { - int max_len = (log_buffer+sizeof(log_buffer))-log_buffer_ptr-1; + int max_len = (int)((log_buffer+sizeof(log_buffer))-log_buffer_ptr-1); PJ_UNUSED_ARG(level); /* make sure len is correct */ - len = strlen(data); + len = (int)strlen(data); if (len > max_len) len = max_len; @@ -638,7 +638,7 @@ int unittest_parallel_test() int unittest_test(void) { - int ret, log_level = pj_log_get_level(); + int ret = 0, log_level = pj_log_get_level(); unsigned j; if (pj_test_is_under_test()) { diff --git a/pjmedia/src/pjmedia/sdp.c b/pjmedia/src/pjmedia/sdp.c index a159721b29..ffc0413040 100644 --- a/pjmedia/src/pjmedia/sdp.c +++ b/pjmedia/src/pjmedia/sdp.c @@ -802,7 +802,7 @@ PJ_DEF(int) pjmedia_sdp_media_print(const pjmedia_sdp_media *media, PJ_DEF(int) pjmedia_sdp_attr_print(const pjmedia_sdp_attr *attr, char *buf, pj_size_t size) { - return print_attr(attr, buf, size); + return (int)print_attr(attr, buf, size); } PJ_DEF(pjmedia_sdp_media*) pjmedia_sdp_media_clone( diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index 570404cfc9..f2cd3ecc5a 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -998,7 +998,8 @@ static void create_dtmf_payload(pjmedia_stream *stream, if (!pj_stricmp2(&stream->si.fmt.encoding_name, "opus")) { ts_modifier = (float)48000 / stream->codec_param.info.clock_rate; } - duration = ts_modifier * digit->send_duration * stream->codec_param.info.clock_rate / 1000; + duration = (unsigned)(ts_modifier * digit->send_duration * + stream->codec_param.info.clock_rate / 1000); } else { diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c index e93c3d6551..dafb7b8672 100644 --- a/pjsip/src/pjsip-ua/sip_inv.c +++ b/pjsip/src/pjsip-ua/sip_inv.c @@ -204,13 +204,14 @@ static pj_status_t add_reason_warning_hdr(pjsip_tx_data *tdata, pjsip_hdr *hdr; const pj_str_t hname = { "Reason", 6 }; pj_str_t hvalue; - unsigned hval_len; + pj_size_t hval_len; + PJ_ASSERT_RETURN(tdata && reason && reason->slen >= 0, PJ_EINVAL); PJ_ASSERT_RETURN(code < 1000, PJ_EINVAL); - hval_len = 3 + /* 'SIP' */ - 11 + /* ' ;cause=3-digit-code' */ - reason->slen + 10; /* ' ;text=".."' */ + hval_len = 3 + /* 'SIP' */ + 11 + /* ' ;cause=3-digit-code' */ + (pj_size_t)reason->slen + 10; /* ' ;text=".."' */ hvalue.ptr = (char*)pj_pool_alloc(tdata->pool, hval_len); if (!hvalue.ptr) return PJ_ENOMEM; diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c index 9951c6859f..bfccc931eb 100644 --- a/pjsip/src/pjsip/sip_auth_client.c +++ b/pjsip/src/pjsip/sip_auth_client.c @@ -69,7 +69,7 @@ #define DEFINE_HASH_CONTEXT pj_md5_context pmc; pj_md5_context* mdctx = &pmc #define EVP_get_digestbyname(digest_name) (digest_name) -#define EVP_MD_CTX_new(mdctx) &pmc +#define EVP_MD_CTX_new() &pmc #define EVP_DigestInit_ex(mdctx, md, _unused) (void)md; pj_md5_init(mdctx) #define EVP_DigestUpdate(mdctx, data, len) MD5_APPEND(mdctx, data, len) #define EVP_DigestFinal_ex(mdctx, digest, _unused) pj_md5_final(mdctx, digest) diff --git a/pjsip/src/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c index 4e408f75cf..6f8ddb2abe 100644 --- a/pjsip/src/pjsip/sip_msg.c +++ b/pjsip/src/pjsip/sip_msg.c @@ -2161,9 +2161,14 @@ PJ_DEF(pjsip_warning_hdr*) pjsip_warning_hdr_create( pj_pool_t *pool, { const pj_str_t str_warning = { "Warning", 7 }; pj_str_t hvalue; - unsigned buflen = 10 + /* code */ - host->slen + 2 + /* host */ - text->slen + 2; /* text */ + unsigned buflen; + + PJ_ASSERT_RETURN(pool && host && text, NULL); + PJ_ASSERT_RETURN(host->slen >= 0 && text->slen >= 0, NULL); + + buflen = 10 + /* code */ + (unsigned)host->slen + 2 + /* host */ + (unsigned)text->slen + 2; /* text */ hvalue.ptr = (char*) pj_pool_alloc(pool, buflen); hvalue.slen = pj_ansi_snprintf(hvalue.ptr, buflen, diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index 4c92fb6b57..59dd5300fc 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -388,6 +388,8 @@ static int get_media_ip_version(pjsua_call_media *call_med, #else PJ_UNUSED_ARG(call_med); PJ_UNUSED_ARG(rem_sdp); + PJ_UNUSED_ARG(loop_tp); + PJ_UNUSED_ARG(ice_tp); #endif /* Use IPv4. */ diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c index 083782e062..9299312c68 100644 --- a/pjsip/src/pjsua-lib/pjsua_pres.c +++ b/pjsip/src/pjsua-lib/pjsua_pres.c @@ -324,7 +324,7 @@ PJ_DEF(pj_status_t) pjsua_buddy_get_dlg_event_info( pjsua_buddy_id buddy_id, pjsua_buddy_dlg_event_info *info) { - unsigned total=0; + pj_ssize_t total=0; struct buddy_lock lck; pjsua_buddy *buddy; pj_status_t status; diff --git a/pjsip/src/pjsua2/endpoint.cpp b/pjsip/src/pjsua2/endpoint.cpp index 3a2b381ef7..559e7aa345 100644 --- a/pjsip/src/pjsua2/endpoint.cpp +++ b/pjsip/src/pjsua2/endpoint.cpp @@ -2517,17 +2517,16 @@ void Endpoint::codecSetParam(const string &codec_id, CodecOpusConfig Endpoint::getCodecOpusConfig() const PJSUA2_THROW(Error) { - CodecOpusConfig config; #if defined(PJMEDIA_HAS_OPUS_CODEC) && (PJMEDIA_HAS_OPUS_CODEC!=0) + CodecOpusConfig config; pjmedia_codec_opus_config opus_cfg; PJSUA2_CHECK_EXPR(pjmedia_codec_opus_get_config(&opus_cfg)); config.fromPj(opus_cfg); + return config; #else PJSUA2_RAISE_ERROR(PJ_ENOTSUP); #endif - - return config; } void Endpoint::setCodecOpusConfig(const CodecOpusConfig &opus_cfg) @@ -2552,17 +2551,16 @@ void Endpoint::setCodecOpusConfig(const CodecOpusConfig &opus_cfg) CodecLyraConfig Endpoint::getCodecLyraConfig() const PJSUA2_THROW(Error) { - CodecLyraConfig config; #if defined(PJMEDIA_HAS_LYRA_CODEC) && (PJMEDIA_HAS_LYRA_CODEC!=0) + CodecLyraConfig config; pjmedia_codec_lyra_config lyra_cfg; PJSUA2_CHECK_EXPR(pjmedia_codec_lyra_get_config(&lyra_cfg)); config.fromPj(lyra_cfg); + return config; #else PJSUA2_RAISE_ERROR(PJ_ENOTSUP); #endif - - return config; } void Endpoint::setCodecLyraConfig(const CodecLyraConfig &lyra_cfg) diff --git a/pjsip/src/pjsua2/media.cpp b/pjsip/src/pjsua2/media.cpp index 2b9ff4a8f0..3b6667644c 100644 --- a/pjsip/src/pjsua2/media.cpp +++ b/pjsip/src/pjsua2/media.cpp @@ -71,19 +71,19 @@ pjmedia_format MediaFormatAudio::toPj() const } void MediaFormatAudio::init(pj_uint32_t formatId, - unsigned clockRate, unsigned channelCount, - int frameTimeUsec, int bitsPerSample, - pj_uint32_t avgBps, pj_uint32_t maxBps) + unsigned clockRate_, unsigned channelCount_, + int frameTimeUsec_, int bitsPerSample_, + pj_uint32_t avgBps_, pj_uint32_t maxBps_) { type = PJMEDIA_TYPE_AUDIO; id = formatId; - this->clockRate = clockRate; - this->channelCount = channelCount; - this->frameTimeUsec = frameTimeUsec; - this->bitsPerSample = bitsPerSample; - this->avgBps = avgBps; - this->maxBps = maxBps; + this->clockRate = clockRate_; + this->channelCount = channelCount_; + this->frameTimeUsec = frameTimeUsec_; + this->bitsPerSample = bitsPerSample_; + this->avgBps = avgBps_; + this->maxBps = maxBps_; } /////////////////////////////////////////////////////////////////////////////// @@ -301,7 +301,7 @@ static pj_status_t get_frame(pjmedia_port *port, pjmedia_frame *frame) AudioMediaPort *mport = (AudioMediaPort *) port->port_data.pdata; MediaFrame frame_; - frame_.size = frame->size; + frame_.size = (unsigned)frame->size; mport->onFrameRequested(frame_); frame->type = frame_.type; frame->size = PJ_MIN(frame_.buf.size(), frame_.size); @@ -326,7 +326,7 @@ static pj_status_t put_frame(pjmedia_port *port, pjmedia_frame *frame) frame_.type = frame->type; frame_.buf.assign((char *)frame->buf, ((char *)frame->buf) + frame->size); - frame_.size = frame->size; + frame_.size = (unsigned)frame->size; mport->onFrameReceived(frame_); return PJ_SUCCESS; @@ -1576,20 +1576,20 @@ pjmedia_format MediaFormatVideo::toPj() const } void MediaFormatVideo::init(pj_uint32_t formatId, - unsigned width, unsigned height, - int fpsNum, int fpsDenum, - pj_uint32_t avgBps, pj_uint32_t maxBps) + unsigned width_, unsigned height_, + int fpsNum_, int fpsDenum_, + pj_uint32_t avgBps_, pj_uint32_t maxBps_) { #if PJSUA_HAS_VIDEO type = PJMEDIA_TYPE_VIDEO; id = formatId; - this->width = width; - this->height = height; - this->fpsNum = fpsNum; - this->fpsDenum = fpsDenum; - this->avgBps = avgBps; - this->maxBps = maxBps; + this->width = width_; + this->height = height_; + this->fpsNum = fpsNum_; + this->fpsDenum = fpsDenum_; + this->avgBps = avgBps_; + this->maxBps = maxBps_; #else type = PJMEDIA_TYPE_UNKNOWN; #endif diff --git a/pjsip/src/pjsua2/siptypes.cpp b/pjsip/src/pjsua2/siptypes.cpp index db9ac9d5d7..279934322d 100644 --- a/pjsip/src/pjsua2/siptypes.cpp +++ b/pjsip/src/pjsua2/siptypes.cpp @@ -332,7 +332,7 @@ pj_sockopt_params SockOptParams::toPj() const unsigned i; pj_bzero(&sop, sizeof(sop)); - sop.cnt = this->sockOpts.size(); + sop.cnt = (unsigned)this->sockOpts.size(); if (sop.cnt > PJ_MAX_SOCKOPT_PARAMS) sop.cnt = PJ_MAX_SOCKOPT_PARAMS; for (i = 0; i < sop.cnt; ++i) { diff --git a/pjsip/src/test/transport_loop_test.c b/pjsip/src/test/transport_loop_test.c index 42c8ed2543..40175160c5 100644 --- a/pjsip/src/test/transport_loop_test.c +++ b/pjsip/src/test/transport_loop_test.c @@ -26,6 +26,9 @@ static void send_cb(pjsip_send_state *st, pj_ssize_t sent, pj_bool_t *cont) { int *loop_resolve_status = (int*)st->token; + + PJ_UNUSED_ARG(cont); + if (sent < 0) { /* Success! */ *loop_resolve_status = (int)-sent; diff --git a/third_party/webrtc/PJSIP_NOTES b/third_party/webrtc/PJSIP_NOTES index 7536dabca5..4904e0b78a 100644 --- a/third_party/webrtc/PJSIP_NOTES +++ b/third_party/webrtc/PJSIP_NOTES @@ -1,5 +1,7 @@ Local changes: + 1. ARM64 support build for Visual Studio. + diff --git a/third_party/webrtc/src/webrtc/typedefs.h b/third_party/webrtc/src/webrtc/typedefs.h index 3034c7e74..9385befc9 100644 --- a/third_party/webrtc/src/webrtc/typedefs.h @@ -13,3 +15,20 @@ index 3034c7e74..9385befc9 100644 #define WEBRTC_ARCH_64_BITS #define WEBRTC_ARCH_LITTLE_ENDIAN #elif defined(_M_IX86) || defined(__i386__) + + +2. Add include pj_config.h for PJSIP specific settings. + +diff --git a/third_party/webrtc/src/webrtc/typedefs.h b/third_party/webrtc/src/webrtc/typedefs.h +index 9385befc9..3460ec880 100644 +--- a/third_party/webrtc/src/webrtc/typedefs.h ++++ b/third_party/webrtc/src/webrtc/typedefs.h +@@ -14,6 +14,8 @@ + #ifndef WEBRTC_TYPEDEFS_H_ + #define WEBRTC_TYPEDEFS_H_ + ++#include "pj_config.h" ++ + // Processor architecture detection. For more info on what's defined, see: + // http://msdn.microsoft.com/en-us/library/b0084kay.aspx + // http://www.agner.org/optimize/calling_conventions.pdf \ No newline at end of file diff --git a/third_party/webrtc/src/pj_config.h b/third_party/webrtc/src/pj_config.h new file mode 100644 index 0000000000..248b40e958 --- /dev/null +++ b/third_party/webrtc/src/pj_config.h @@ -0,0 +1,9 @@ +#ifndef PJ_CONFIG_H +#define PJ_CONFIG_H + +#if defined(_MSC_VER) +# pragma warning(disable: 4267) // conversion from 'size_t' to 'int', possible loss of data +# pragma warning(disable: 4334) // '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) +#endif + +#endif \ No newline at end of file diff --git a/third_party/webrtc/src/webrtc/typedefs.h b/third_party/webrtc/src/webrtc/typedefs.h index 9385befc96..3460ec8807 100644 --- a/third_party/webrtc/src/webrtc/typedefs.h +++ b/third_party/webrtc/src/webrtc/typedefs.h @@ -14,6 +14,8 @@ #ifndef WEBRTC_TYPEDEFS_H_ #define WEBRTC_TYPEDEFS_H_ +#include "pj_config.h" + // Processor architecture detection. For more info on what's defined, see: // http://msdn.microsoft.com/en-us/library/b0084kay.aspx // http://www.agner.org/optimize/calling_conventions.pdf From e1a878252b05c26bb174f487937365b3027fc9d0 Mon Sep 17 00:00:00 2001 From: sauwming Date: Mon, 25 Nov 2024 15:07:30 +0800 Subject: [PATCH 118/491] Fixed configure error for SDL cross compilation (#4175) --- aconfigure | 2219 +++++++++++++++++++++++++++++-------------------- aconfigure.ac | 2 +- 2 files changed, 1329 insertions(+), 892 deletions(-) diff --git a/aconfigure b/aconfigure index d5d587bfcd..d1b3dba45a 100755 --- a/aconfigure +++ b/aconfigure @@ -1,9 +1,9 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for pjproject 2.x. +# Generated by GNU Autoconf 2.72 for pjproject 2.x. # # -# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, +# Copyright (C) 1992-1996, 1998-2017, 2020-2023 Free Software Foundation, # Inc. # # @@ -15,7 +15,6 @@ # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh -as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh @@ -24,12 +23,13 @@ then : # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST -else $as_nop - case `(set -o) 2>/dev/null` in #( +else case e in #( + e) case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; +esac ;; esac fi @@ -101,7 +101,7 @@ IFS=$as_save_IFS ;; esac -# We did not find ourselves, most probably we were run as `sh COMMAND' +# We did not find ourselves, most probably we were run as 'sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 @@ -131,15 +131,14 @@ case $- in # (((( esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. +# out after a failed 'exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then - as_bourne_compatible="as_nop=: -if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 + as_bourne_compatible="if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: @@ -147,12 +146,13 @@ then : # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST -else \$as_nop - case \`(set -o) 2>/dev/null\` in #( +else case e in #( + e) case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; +esac ;; esac fi " @@ -170,8 +170,9 @@ as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ) then : -else \$as_nop - exitcode=1; echo positional parameters were not saved. +else case e in #( + e) exitcode=1; echo positional parameters were not saved. ;; +esac fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) @@ -184,14 +185,15 @@ test -x / || exit 1" if (eval "$as_required") 2>/dev/null then : as_have_required=yes -else $as_nop - as_have_required=no +else case e in #( + e) as_have_required=no ;; +esac fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : -else $as_nop - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +else case e in #( + e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do @@ -224,12 +226,13 @@ IFS=$as_save_IFS if $as_found then : -else $as_nop - if { test -f "$SHELL" || test -f "$SHELL.exe"; } && +else case e in #( + e) if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes -fi +fi ;; +esac fi @@ -251,7 +254,7 @@ case $- in # (((( esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. +# out after a failed 'exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi @@ -270,7 +273,8 @@ $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 -fi +fi ;; +esac fi fi SHELL=${CONFIG_SHELL-/bin/sh} @@ -309,14 +313,6 @@ as_fn_exit () as_fn_set_status $1 exit $1 } # as_fn_exit -# as_fn_nop -# --------- -# Do nothing but, unlike ":", preserve the value of $?. -as_fn_nop () -{ - return $? -} -as_nop=as_fn_nop # as_fn_mkdir_p # ------------- @@ -385,11 +381,12 @@ then : { eval $1+=\$2 }' -else $as_nop - as_fn_append () +else case e in #( + e) as_fn_append () { eval $1=\$$1\$2 - } + } ;; +esac fi # as_fn_append # as_fn_arith ARG... @@ -403,21 +400,14 @@ then : { as_val=$(( $* )) }' -else $as_nop - as_fn_arith () +else case e in #( + e) as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` - } + } ;; +esac fi # as_fn_arith -# as_fn_nop -# --------- -# Do nothing but, unlike ":", preserve the value of $?. -as_fn_nop () -{ - return $? -} -as_nop=as_fn_nop # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- @@ -491,6 +481,8 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits /[$]LINENO/= ' <$as_myself | sed ' + t clear + :clear s/[$]LINENO.*/&-/ t lineno b @@ -539,7 +531,6 @@ esac as_echo='printf %s\n' as_echo_n='printf %s' - rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file @@ -551,9 +542,9 @@ if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. + # 1) On MSYS, both 'ln -s file dir' and 'ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; 'ln -s' creates a wrapper executable. + # In both cases, we have to default to 'cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then @@ -578,10 +569,12 @@ as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" +as_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" +as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated # Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" +as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g" +as_tr_sh="eval sed '$as_sed_sh'" # deprecated test -n "$DJDIR" || exec 7<&0 /dev/null && - as_fn_error $? "invalid feature name: \`$ac_useropt'" + as_fn_error $? "invalid feature name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1039,7 +1032,7 @@ do ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: \`$ac_useropt'" + as_fn_error $? "invalid feature name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1252,7 +1245,7 @@ do ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: \`$ac_useropt'" + as_fn_error $? "invalid package name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1268,7 +1261,7 @@ do ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: \`$ac_useropt'" + as_fn_error $? "invalid package name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1298,8 +1291,8 @@ do | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; - -*) as_fn_error $? "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information" + -*) as_fn_error $? "unrecognized option: '$ac_option' +Try '$0 --help' for more information" ;; *=*) @@ -1307,7 +1300,7 @@ Try \`$0 --help' for more information" # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + as_fn_error $? "invalid variable name: '$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; @@ -1357,7 +1350,7 @@ do as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done -# There might be people who depend on the old broken behavior: `$host' +# There might be people who depend on the old broken behavior: '$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias @@ -1425,7 +1418,7 @@ if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi -ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_msg="sources are in $srcdir, but 'cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` @@ -1453,7 +1446,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures pjproject 2.x to adapt to many kinds of systems. +'configure' configures pjproject 2.x to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1467,11 +1460,11 @@ Configuration: --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking ...' messages + -q, --quiet, --silent do not print 'checking ...' messages --cache-file=FILE cache test results in FILE [disabled] - -C, --config-cache alias for \`--cache-file=config.cache' + -C, --config-cache alias for '--cache-file=config.cache' -n, --no-create do not create output files - --srcdir=DIR find the sources in DIR [configure dir or \`..'] + --srcdir=DIR find the sources in DIR [configure dir or '..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX @@ -1479,10 +1472,10 @@ Installation directories: --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] -By default, \`make install' will install all the files in -\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify -an installation prefix other than \`$ac_default_prefix' using \`--prefix', -for instance \`--prefix=\$HOME'. +By default, 'make install' will install all the files in +'$ac_default_prefix/bin', '$ac_default_prefix/lib' etc. You can specify +an installation prefix other than '$ac_default_prefix' using '--prefix', +for instance '--prefix=\$HOME'. For better control, use the options below. @@ -1655,7 +1648,7 @@ Some influential environment variables: CXX C++ compiler command CXXFLAGS C++ compiler flags -Use these variables to override the choices made by `configure' or to help +Use these variables to override the choices made by 'configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. @@ -1723,9 +1716,9 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF pjproject configure 2.x -generated by GNU Autoconf 2.71 +generated by GNU Autoconf 2.72 -Copyright (C) 2021 Free Software Foundation, Inc. +Copyright (C) 2023 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF @@ -1764,11 +1757,12 @@ printf "%s\n" "$ac_try_echo"; } >&5 } && test -s conftest.$ac_objext then : ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 +else case e in #( + e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_retval=1 + ac_retval=1 ;; +esac fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval @@ -1803,11 +1797,12 @@ printf "%s\n" "$ac_try_echo"; } >&5 } && test -s conftest.$ac_objext then : ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 +else case e in #( + e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_retval=1 + ac_retval=1 ;; +esac fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval @@ -1845,11 +1840,12 @@ printf "%s\n" "$ac_try_echo"; } >&5 } then : ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 +else case e in #( + e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_retval=1 + ac_retval=1 ;; +esac fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would @@ -1892,11 +1888,12 @@ printf "%s\n" "$ac_try_echo"; } >&5 } then : ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 +else case e in #( + e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_retval=1 + ac_retval=1 ;; +esac fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would @@ -1938,12 +1935,13 @@ printf "%s\n" "$ac_try_echo"; } >&5 test $ac_status = 0; }; } then : ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: program exited with status $ac_status" >&5 +else case e in #( + e) printf "%s\n" "$as_me: program exited with status $ac_status" >&5 printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_retval=$ac_status + ac_retval=$ac_status ;; +esac fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno @@ -1963,8 +1961,8 @@ printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> @@ -1972,10 +1970,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" -else $as_nop - eval "$3=no" +else case e in #( + e) eval "$3=no" ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 @@ -1995,15 +1995,15 @@ printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $2 (); below. */ + which can conflict with char $2 (void); below. */ #include #undef $2 @@ -2014,7 +2014,7 @@ else $as_nop #ifdef __cplusplus extern "C" #endif -char $2 (); +char $2 (void); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ @@ -2033,11 +2033,13 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$3=yes" -else $as_nop - eval "$3=no" +else case e in #( + e) eval "$3=no" ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext + conftest$ac_exeext conftest.$ac_ext ;; +esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 @@ -2058,8 +2060,8 @@ printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 -else $as_nop - eval "$3=no" +else case e in #( + e) eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 @@ -2089,12 +2091,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : -else $as_nop - eval "$3=yes" +else case e in #( + e) eval "$3=yes" ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 @@ -2127,7 +2131,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by pjproject $as_me 2.x, which was -generated by GNU Autoconf 2.71. Invocation command line was +generated by GNU Autoconf 2.72. Invocation command line was $ $0$ac_configure_args_raw @@ -2373,10 +2377,10 @@ esac printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ - || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } fi done @@ -2412,9 +2416,7 @@ struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; +static char *e (char **p, int i) { return p[i]; } @@ -2428,6 +2430,21 @@ static char *f (char * (*g) (char **, int), char **p, ...) return s; } +/* C89 style stringification. */ +#define noexpand_stringify(a) #a +const char *stringified = noexpand_stringify(arbitrary+token=sequence); + +/* C89 style token pasting. Exercises some of the corner cases that + e.g. old MSVC gets wrong, but not very hard. */ +#define noexpand_concat(a,b) a##b +#define expand_concat(a,b) noexpand_concat(a,b) +extern int vA; +extern int vbee; +#define aye A +#define bee B +int *pvA = &expand_concat(v,aye); +int *pvbee = &noexpand_concat(v,bee); + /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not \xHH hex character constants. These do not provoke an error unfortunately, instead are silently treated @@ -2455,16 +2472,19 @@ ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' -// Does the compiler advertise C99 conformance? +/* Does the compiler advertise C99 conformance? */ #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif +// See if C++-style comments work. + #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); +extern void free (void *); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare @@ -2514,7 +2534,6 @@ typedef const char *ccp; static inline int test_restrict (ccp restrict text) { - // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) @@ -2580,6 +2599,8 @@ ac_c_conftest_c99_main=' ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; + // Work around memory leak warnings. + free (ia); // Check named initializers. struct named_init ni = { @@ -2601,7 +2622,7 @@ ac_c_conftest_c99_main=' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' -// Does the compiler advertise C11 conformance? +/* Does the compiler advertise C11 conformance? */ #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif @@ -3009,8 +3030,9 @@ IFS=$as_save_IFS if $as_found then : -else $as_nop - as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 +else case e in #( + e) as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 ;; +esac fi @@ -3038,12 +3060,12 @@ for ac_var in $ac_precious_vars; do eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&5 +printf "%s\n" "$as_me: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 -printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was not set in the previous run" >&5 +printf "%s\n" "$as_me: error: '$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) @@ -3052,18 +3074,18 @@ printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 -printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' has changed since the previous run:" >&5 +printf "%s\n" "$as_me: error: '$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 -printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&5 +printf "%s\n" "$as_me: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 -printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 -printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: '$ac_old_val'" >&5 +printf "%s\n" "$as_me: former value: '$ac_old_val'" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: '$ac_new_val'" >&5 +printf "%s\n" "$as_me: current value: '$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. @@ -3079,11 +3101,11 @@ printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} fi done if $ac_cache_corrupted; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' + as_fn_error $? "run '${MAKE-make} distclean' and/or 'rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## @@ -3112,15 +3134,16 @@ printf %s "checking build system type... " >&6; } if test ${ac_cv_build+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_build_alias=$build_alias +else case e in #( + e) ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5 - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 printf "%s\n" "$ac_cv_build" >&6; } @@ -3147,14 +3170,15 @@ printf %s "checking host system type... " >&6; } if test ${ac_cv_host+y} then : printf %s "(cached) " >&6 -else $as_nop - if test "x$host_alias" = x; then +else case e in #( + e) if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5 fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 printf "%s\n" "$ac_cv_host" >&6; } @@ -3181,14 +3205,15 @@ printf %s "checking target system type... " >&6; } if test ${ac_cv_target+y} then : printf %s "(cached) " >&6 -else $as_nop - if test "x$target_alias" = x; then +else case e in #( + e) if test "x$target_alias" = x; then ac_cv_target=$ac_cv_host else ac_cv_target=`$SHELL "${ac_aux_dir}config.sub" $target_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $target_alias failed" "$LINENO" 5 fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 printf "%s\n" "$ac_cv_target" >&6; } @@ -3248,8 +3273,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then +else case e in #( + e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -3271,7 +3296,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then @@ -3293,8 +3319,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_CC"; then +else case e in #( + e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -3316,7 +3342,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then @@ -3351,8 +3378,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then +else case e in #( + e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -3374,7 +3401,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then @@ -3396,8 +3424,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then +else case e in #( + e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no @@ -3436,7 +3464,8 @@ if test $ac_prog_rejected = yes; then ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" fi fi -fi +fi ;; +esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then @@ -3460,8 +3489,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then +else case e in #( + e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -3483,7 +3512,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then @@ -3509,8 +3539,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_CC"; then +else case e in #( + e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -3532,7 +3562,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then @@ -3570,8 +3601,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then +else case e in #( + e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -3593,7 +3624,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then @@ -3615,8 +3647,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_CC"; then +else case e in #( + e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -3638,7 +3670,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then @@ -3667,10 +3700,10 @@ fi fi -test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 @@ -3742,8 +3775,8 @@ printf "%s\n" "$ac_try_echo"; } >&5 printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : - # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. -# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' + # Autoconf-2.13 could set the ac_cv_exeext variable to 'no'. +# So ignore a value of 'no', otherwise this would lead to 'EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. @@ -3763,7 +3796,7 @@ do ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not - # safe: cross compilers may not add the suffix if given an `-o' + # safe: cross compilers may not add the suffix if given an '-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. @@ -3774,8 +3807,9 @@ do done test "$ac_cv_exeext" = no && ac_cv_exeext= -else $as_nop - ac_file='' +else case e in #( + e) ac_file='' ;; +esac fi if test -z "$ac_file" then : @@ -3784,13 +3818,14 @@ printf "%s\n" "no" >&6; } printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -printf "%s\n" "yes" >&6; } +See 'config.log' for more details" "$LINENO" 5; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 printf %s "checking for C compiler default output file name... " >&6; } @@ -3814,10 +3849,10 @@ printf "%s\n" "$ac_try_echo"; } >&5 printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : - # If both `conftest.exe' and `conftest' are `present' (well, observable) -# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will -# work properly (i.e., refer to `conftest.exe'), while it won't with -# `rm'. + # If both 'conftest.exe' and 'conftest' are 'present' (well, observable) +# catch 'conftest.exe'. For instance with Cygwin, 'ls conftest' will +# work properly (i.e., refer to 'conftest.exe'), while it won't with +# 'rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in @@ -3827,11 +3862,12 @@ for ac_file in conftest.exe conftest conftest.*; do * ) break;; esac done -else $as_nop - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } ;; +esac fi rm -f conftest conftest$ac_cv_exeext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 @@ -3847,6 +3883,8 @@ int main (void) { FILE *f = fopen ("conftest.out", "w"); + if (!f) + return 1; return ferror (f) || fclose (f) != 0; ; @@ -3886,26 +3924,27 @@ printf "%s\n" "$ac_try_echo"; } >&5 if test "$cross_compiling" = maybe; then cross_compiling=yes else - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5; } +If you meant to cross compile, use '--host'. +See 'config.log' for more details" "$LINENO" 5; } fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } -rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +rm -f conftest.$ac_ext conftest$ac_cv_exeext \ + conftest.o conftest.obj conftest.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -3937,16 +3976,18 @@ then : break;; esac done -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 +else case e in #( + e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } ;; +esac fi -rm -f conftest.$ac_cv_objext conftest.$ac_ext +rm -f conftest.$ac_cv_objext conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf "%s\n" "$ac_cv_objext" >&6; } @@ -3957,8 +3998,8 @@ printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -3975,12 +4016,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes -else $as_nop - ac_compiler_gnu=no +else case e in #( + e) ac_compiler_gnu=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } @@ -3998,8 +4041,8 @@ printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_save_c_werror_flag=$ac_c_werror_flag +else case e in #( + e) ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" @@ -4017,8 +4060,8 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes -else $as_nop - CFLAGS="" +else case e in #( + e) CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -4033,8 +4076,8 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : -else $as_nop - ac_c_werror_flag=$ac_save_c_werror_flag +else case e in #( + e) ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -4051,12 +4094,15 @@ if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag + ac_c_werror_flag=$ac_save_c_werror_flag ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } @@ -4083,8 +4129,8 @@ printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_cv_prog_cc_c11=no +else case e in #( + e) ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -4101,25 +4147,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext -CC=$ac_save_CC +CC=$ac_save_CC ;; +esac fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } -else $as_nop - if test "x$ac_cv_prog_cc_c11" = x +else case e in #( + e) if test "x$ac_cv_prog_cc_c11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } - CC="$CC $ac_cv_prog_cc_c11" + CC="$CC $ac_cv_prog_cc_c11" ;; +esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 - ac_prog_cc_stdc=c11 + ac_prog_cc_stdc=c11 ;; +esac fi fi if test x$ac_prog_cc_stdc = xno @@ -4129,8 +4178,8 @@ printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_cv_prog_cc_c99=no +else case e in #( + e) ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -4147,25 +4196,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext -CC=$ac_save_CC +CC=$ac_save_CC ;; +esac fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } -else $as_nop - if test "x$ac_cv_prog_cc_c99" = x +else case e in #( + e) if test "x$ac_cv_prog_cc_c99" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } - CC="$CC $ac_cv_prog_cc_c99" + CC="$CC $ac_cv_prog_cc_c99" ;; +esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 - ac_prog_cc_stdc=c99 + ac_prog_cc_stdc=c99 ;; +esac fi fi if test x$ac_prog_cc_stdc = xno @@ -4175,8 +4227,8 @@ printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_cv_prog_cc_c89=no +else case e in #( + e) ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -4193,25 +4245,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext -CC=$ac_save_CC +CC=$ac_save_CC ;; +esac fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } -else $as_nop - if test "x$ac_cv_prog_cc_c89" = x +else case e in #( + e) if test "x$ac_cv_prog_cc_c89" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } - CC="$CC $ac_cv_prog_cc_c89" + CC="$CC $ac_cv_prog_cc_c89" ;; +esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 - ac_prog_cc_stdc=c89 + ac_prog_cc_stdc=c89 ;; +esac fi fi @@ -4246,8 +4301,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CXX+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$CXX"; then +else case e in #( + e) if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -4269,7 +4324,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then @@ -4295,8 +4351,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CXX+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_CXX"; then +else case e in #( + e) if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -4318,7 +4374,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then @@ -4378,8 +4435,8 @@ printf %s "checking whether the compiler supports GNU C++... " >&6; } if test ${ac_cv_cxx_compiler_gnu+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -4396,12 +4453,14 @@ _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : ac_compiler_gnu=yes -else $as_nop - ac_compiler_gnu=no +else case e in #( + e) ac_compiler_gnu=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 printf "%s\n" "$ac_cv_cxx_compiler_gnu" >&6; } @@ -4419,8 +4478,8 @@ printf %s "checking whether $CXX accepts -g... " >&6; } if test ${ac_cv_prog_cxx_g+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_save_cxx_werror_flag=$ac_cxx_werror_flag +else case e in #( + e) ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" @@ -4438,8 +4497,8 @@ _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : ac_cv_prog_cxx_g=yes -else $as_nop - CXXFLAGS="" +else case e in #( + e) CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -4454,8 +4513,8 @@ _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : -else $as_nop - ac_cxx_werror_flag=$ac_save_cxx_werror_flag +else case e in #( + e) ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -4472,12 +4531,15 @@ if ac_fn_cxx_try_compile "$LINENO" then : ac_cv_prog_cxx_g=yes fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - ac_cxx_werror_flag=$ac_save_cxx_werror_flag + ac_cxx_werror_flag=$ac_save_cxx_werror_flag ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 printf "%s\n" "$ac_cv_prog_cxx_g" >&6; } @@ -4504,8 +4566,8 @@ printf %s "checking for $CXX option to enable C++11 features... " >&6; } if test ${ac_cv_prog_cxx_cxx11+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_cv_prog_cxx_cxx11=no +else case e in #( + e) ac_cv_prog_cxx_cxx11=no ac_save_CXX=$CXX cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -4522,25 +4584,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cxx_cxx11" != "xno" && break done rm -f conftest.$ac_ext -CXX=$ac_save_CXX +CXX=$ac_save_CXX ;; +esac fi if test "x$ac_cv_prog_cxx_cxx11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } -else $as_nop - if test "x$ac_cv_prog_cxx_cxx11" = x +else case e in #( + e) if test "x$ac_cv_prog_cxx_cxx11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx11" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx11" >&5 printf "%s\n" "$ac_cv_prog_cxx_cxx11" >&6; } - CXX="$CXX $ac_cv_prog_cxx_cxx11" + CXX="$CXX $ac_cv_prog_cxx_cxx11" ;; +esac fi ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx11 - ac_prog_cxx_stdcxx=cxx11 + ac_prog_cxx_stdcxx=cxx11 ;; +esac fi fi if test x$ac_prog_cxx_stdcxx = xno @@ -4550,8 +4615,8 @@ printf %s "checking for $CXX option to enable C++98 features... " >&6; } if test ${ac_cv_prog_cxx_cxx98+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_cv_prog_cxx_cxx98=no +else case e in #( + e) ac_cv_prog_cxx_cxx98=no ac_save_CXX=$CXX cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -4568,25 +4633,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cxx_cxx98" != "xno" && break done rm -f conftest.$ac_ext -CXX=$ac_save_CXX +CXX=$ac_save_CXX ;; +esac fi if test "x$ac_cv_prog_cxx_cxx98" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } -else $as_nop - if test "x$ac_cv_prog_cxx_cxx98" = x +else case e in #( + e) if test "x$ac_cv_prog_cxx_cxx98" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx98" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx98" >&5 printf "%s\n" "$ac_cv_prog_cxx_cxx98" >&6; } - CXX="$CXX $ac_cv_prog_cxx_cxx98" + CXX="$CXX $ac_cv_prog_cxx_cxx98" ;; +esac fi ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx98 - ac_prog_cxx_stdcxx=cxx98 + ac_prog_cxx_stdcxx=cxx98 ;; +esac fi fi @@ -4624,8 +4692,9 @@ _ACEOF if ac_fn_cxx_try_link "$LINENO" then : -else $as_nop - as_fn_error $? "C++ compiler does not work" "$LINENO" 5 +else case e in #( + e) as_fn_error $? "C++ compiler does not work" "$LINENO" 5 ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -4644,8 +4713,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_RANLIB+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$RANLIB"; then +else case e in #( + e) if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -4667,7 +4736,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then @@ -4689,8 +4759,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_RANLIB+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_RANLIB"; then +else case e in #( + e) if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -4712,7 +4782,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then @@ -4748,8 +4819,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_AR+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$AR"; then +else case e in #( + e) if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -4771,7 +4842,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi AR=$ac_cv_prog_AR if test -n "$AR"; then @@ -4797,8 +4869,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_AR+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_AR"; then +else case e in #( + e) if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -4820,7 +4892,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then @@ -4916,16 +4989,22 @@ printf %s "checking for pthread_create in -lpthread... " >&6; } if test ${ac_cv_lib_pthread_pthread_create+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lpthread $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char pthread_create (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_create (void); int main (void) { @@ -4937,12 +5016,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_pthread_pthread_create=yes -else $as_nop - ac_cv_lib_pthread_pthread_create=no +else case e in #( + e) ac_cv_lib_pthread_pthread_create=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5 printf "%s\n" "$ac_cv_lib_pthread_pthread_create" >&6; } @@ -4959,16 +5040,22 @@ printf %s "checking for puts in -lwsock32... " >&6; } if test ${ac_cv_lib_wsock32_puts+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lwsock32 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char puts (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char puts (void); int main (void) { @@ -4980,12 +5067,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_wsock32_puts=yes -else $as_nop - ac_cv_lib_wsock32_puts=no +else case e in #( + e) ac_cv_lib_wsock32_puts=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_wsock32_puts" >&5 printf "%s\n" "$ac_cv_lib_wsock32_puts" >&6; } @@ -5002,16 +5091,22 @@ printf %s "checking for puts in -lws2_32... " >&6; } if test ${ac_cv_lib_ws2_32_puts+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lws2_32 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char puts (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char puts (void); int main (void) { @@ -5023,12 +5118,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ws2_32_puts=yes -else $as_nop - ac_cv_lib_ws2_32_puts=no +else case e in #( + e) ac_cv_lib_ws2_32_puts=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ws2_32_puts" >&5 printf "%s\n" "$ac_cv_lib_ws2_32_puts" >&6; } @@ -5045,16 +5142,22 @@ printf %s "checking for puts in -lole32... " >&6; } if test ${ac_cv_lib_ole32_puts+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lole32 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char puts (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char puts (void); int main (void) { @@ -5066,12 +5169,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ole32_puts=yes -else $as_nop - ac_cv_lib_ole32_puts=no +else case e in #( + e) ac_cv_lib_ole32_puts=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ole32_puts" >&5 printf "%s\n" "$ac_cv_lib_ole32_puts" >&6; } @@ -5088,16 +5193,22 @@ printf %s "checking for puts in -lwinmm... " >&6; } if test ${ac_cv_lib_winmm_puts+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lwinmm $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char puts (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char puts (void); int main (void) { @@ -5109,12 +5220,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_winmm_puts=yes -else $as_nop - ac_cv_lib_winmm_puts=no +else case e in #( + e) ac_cv_lib_winmm_puts=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_winmm_puts" >&5 printf "%s\n" "$ac_cv_lib_winmm_puts" >&6; } @@ -5131,16 +5244,22 @@ printf %s "checking for puts in -lsocket... " >&6; } if test ${ac_cv_lib_socket_puts+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char puts (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char puts (void); int main (void) { @@ -5152,12 +5271,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_socket_puts=yes -else $as_nop - ac_cv_lib_socket_puts=no +else case e in #( + e) ac_cv_lib_socket_puts=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_puts" >&5 printf "%s\n" "$ac_cv_lib_socket_puts" >&6; } @@ -5174,16 +5295,22 @@ printf %s "checking for puts in -lrt... " >&6; } if test ${ac_cv_lib_rt_puts+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lrt $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char puts (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char puts (void); int main (void) { @@ -5195,12 +5322,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_rt_puts=yes -else $as_nop - ac_cv_lib_rt_puts=no +else case e in #( + e) ac_cv_lib_rt_puts=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_puts" >&5 printf "%s\n" "$ac_cv_lib_rt_puts" >&6; } @@ -5217,16 +5346,22 @@ printf %s "checking for sin in -lm... " >&6; } if test ${ac_cv_lib_m_sin+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lm $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sin (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char sin (void); int main (void) { @@ -5238,12 +5373,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_m_sin=yes -else $as_nop - ac_cv_lib_m_sin=no +else case e in #( + e) ac_cv_lib_m_sin=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_sin" >&5 printf "%s\n" "$ac_cv_lib_m_sin" >&6; } @@ -5264,8 +5401,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_PKG_CONFIG+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$PKG_CONFIG"; then +else case e in #( + e) if test -n "$PKG_CONFIG"; then ac_cv_prog_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -5287,7 +5424,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi PKG_CONFIG=$ac_cv_prog_PKG_CONFIG if test -n "$PKG_CONFIG"; then @@ -5314,23 +5452,29 @@ printf "%s\n" "Checking if libuuid disabled... yes" >&6; } ac_has_uuid_lib=0 fi -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uuid_generate in -luuid" >&5 printf %s "checking for uuid_generate in -luuid... " >&6; } if test ${ac_cv_lib_uuid_uuid_generate+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-luuid $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char uuid_generate (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char uuid_generate (void); int main (void) { @@ -5342,12 +5486,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_uuid_uuid_generate=yes -else $as_nop - ac_cv_lib_uuid_uuid_generate=no +else case e in #( + e) ac_cv_lib_uuid_uuid_generate=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_uuid_uuid_generate" >&5 printf "%s\n" "$ac_cv_lib_uuid_uuid_generate" >&6; } @@ -5364,16 +5510,22 @@ printf %s "checking for uuid_generate in -luuid... " >&6; } if test ${ac_cv_lib_uuid_uuid_generate+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-luuid $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char uuid_generate (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char uuid_generate (void); int main (void) { @@ -5385,12 +5537,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_uuid_uuid_generate=yes -else $as_nop - ac_cv_lib_uuid_uuid_generate=no +else case e in #( + e) ac_cv_lib_uuid_uuid_generate=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_uuid_uuid_generate" >&5 printf "%s\n" "$ac_cv_lib_uuid_uuid_generate" >&6; } @@ -5400,7 +5554,8 @@ then : fi - + ;; +esac fi @@ -5409,15 +5564,21 @@ printf %s "checking for library containing gethostbyname... " >&6; } if test ${ac_cv_search_gethostbyname+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_func_search_save_LIBS=$LIBS +else case e in #( + e) ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char gethostbyname (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char gethostbyname (void); int main (void) { @@ -5448,11 +5609,13 @@ done if test ${ac_cv_search_gethostbyname+y} then : -else $as_nop - ac_cv_search_gethostbyname=no +else case e in #( + e) ac_cv_search_gethostbyname=no ;; +esac fi rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS +LIBS=$ac_func_search_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostbyname" >&5 printf "%s\n" "$ac_cv_search_gethostbyname" >&6; } @@ -5521,8 +5684,8 @@ printf %s "checking whether byte ordering is bigendian... " >&6; } if test ${ac_cv_c_bigendian+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_cv_c_bigendian=unknown +else case e in #( + e) ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -5568,8 +5731,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext int main (void) { -#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ - && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ +#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \\ + && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \\ && LITTLE_ENDIAN) bogus endian macros #endif @@ -5600,8 +5763,9 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_bigendian=yes -else $as_nop - ac_cv_c_bigendian=no +else case e in #( + e) ac_cv_c_bigendian=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi @@ -5645,8 +5809,9 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_bigendian=yes -else $as_nop - ac_cv_c_bigendian=no +else case e in #( + e) ac_cv_c_bigendian=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi @@ -5673,22 +5838,23 @@ unsigned short int ascii_mm[] = int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } - extern int foo; - -int -main (void) -{ -return use_ascii (foo) == use_ebcdic (foo); - ; - return 0; -} + int + main (int argc, char **argv) + { + /* Intimidate the compiler so that it does not + optimize the arrays away. */ + char *p = argv[0]; + ascii_mm[1] = *p++; ebcdic_mm[1] = *p++; + ascii_ii[1] = *p++; ebcdic_ii[1] = *p++; + return use_ascii (argc) == use_ebcdic (*p); + } _ACEOF -if ac_fn_c_try_compile "$LINENO" +if ac_fn_c_try_link "$LINENO" then : - if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then + if grep BIGenDianSyS conftest$ac_exeext >/dev/null; then ac_cv_c_bigendian=yes fi - if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if grep LiTTleEnDian conftest$ac_exeext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else @@ -5697,9 +5863,10 @@ then : fi fi fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int @@ -5722,14 +5889,17 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_c_bigendian=no -else $as_nop - ac_cv_c_bigendian=yes +else case e in #( + e) ac_cv_c_bigendian=yes ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - fi + fi ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 printf "%s\n" "$ac_cv_c_bigendian" >&6; } @@ -5817,14 +5987,15 @@ then : printf "%s\n" "Checking if floating point is disabled... yes" >&6; } fi -else $as_nop - +else case e in #( + e) printf "%s\n" "#define PJ_HAS_FLOATING_POINT 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if floating point is disabled... no" >&5 printf "%s\n" "Checking if floating point is disabled... no" >&6; } - + ;; +esac fi @@ -6238,10 +6409,11 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6271,10 +6443,11 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6304,10 +6477,11 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6337,10 +6511,11 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6369,10 +6544,11 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6403,10 +6579,11 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6435,10 +6612,11 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6467,10 +6645,11 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6509,10 +6688,11 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; @@ -6543,15 +6723,16 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - +else case e in #( + e) printf "%s\n" "#define PJ_EMULATE_RWMUTEX 1" >>confdefs.h ac_rwmutex="no" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6582,14 +6763,15 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - +else case e in #( + e) printf "%s\n" "#define PJ_EMULATE_RWMUTEX 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi @@ -6617,10 +6799,11 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6647,10 +6830,11 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -6677,12 +6861,13 @@ printf "%s\n" "kqueue()" >&6; } printf "%s\n" "select()" >&6; } fi -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: select()" >&5 printf "%s\n" "select()" >&6; } - + ;; +esac fi ;; @@ -6703,12 +6888,13 @@ printf "%s\n" "/dev/epoll" >&6; } printf "%s\n" "select()" >&6; } fi -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: select()" >&5 printf "%s\n" "select()" >&6; } - + ;; +esac fi ;; @@ -6726,10 +6912,11 @@ then : printf "%s\n" "Building shared libraries... yes" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Building shared libraries... no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Building shared libraries... no" >&5 printf "%s\n" "Building shared libraries... no" >&6; } - + ;; +esac fi @@ -6744,10 +6931,11 @@ then : printf "%s\n" "Building pjsua2 library and application... no" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Building pjsua2 library and application... yes" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Building pjsua2 library and application... yes" >&5 printf "%s\n" "Building pjsua2 library and application... yes" >&6; } - + ;; +esac fi @@ -6812,9 +7000,10 @@ esac if test ${with_upnp+y} then : withval=$with_upnp; -else $as_nop - with_upnp=no - +else case e in #( + e) with_upnp=no + ;; +esac fi @@ -6831,8 +7020,8 @@ then : printf "%s\n" "Checking if UPnP is disabled... yes" >&6; } fi -else $as_nop - +else case e in #( + e) if test "x$with_upnp" != "xno" -a "x$with_upnp" != "x"; then UPNP_PREFIX=$with_upnp UPNP_CFLAGS="-I$UPNP_PREFIX/upnp/inc -I$UPNP_PREFIX/ixml/inc" @@ -6879,20 +7068,22 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - +else case e in #( + e) LIBS="$SAVED_LIBS" LDFLAGS="$SAVED_LDFLAGS" CFLAGS="$SAVED_CFLAGS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi @@ -6932,11 +7123,12 @@ printf "%s\n" "yes!!" >&6; } ac_external_speex="1" -else $as_nop - +else case e in #( + e) as_fn_error $? "Unable to use external Speex library. If Speex development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi @@ -6980,8 +7172,8 @@ printf "%s\n" "yes!!" >&6; } ac_external_gsm="1" -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if external GSM devkit is installed as gsm.h" >&5 @@ -7011,15 +7203,17 @@ printf "%s\n" "yes!!" >&6; } ac_external_gsm="1" -else $as_nop - +else case e in #( + e) as_fn_error $? "Unable to use external GSM library. If GSM development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi @@ -7063,8 +7257,8 @@ printf "%s\n" "yes: version 2.x" >&6; } ac_external_srtp="2" ac_external_srtp_lib="srtp2" -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -7087,15 +7281,17 @@ printf "%s\n" "yes: version 1.x" >&6; } ac_external_srtp="1" ac_external_srtp_lib="srtp" -else $as_nop - +else case e in #( + e) as_fn_error $? "Unable to use SRTP. If SRTP development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi @@ -7109,22 +7305,28 @@ if test "x$ac_external_srtp" != "x0"; then ac_srtp_shutdown_present=0 - as_ac_Lib=`printf "%s\n" "ac_cv_lib_$ac_external_srtp_lib""_srtp_deinit" | $as_tr_sh` + as_ac_Lib=`printf "%s\n" "ac_cv_lib_$ac_external_srtp_lib""_srtp_deinit" | sed "$as_sed_sh"` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for srtp_deinit in -l$ac_external_srtp_lib" >&5 printf %s "checking for srtp_deinit in -l$ac_external_srtp_lib... " >&6; } if eval test \${$as_ac_Lib+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-l$ac_external_srtp_lib $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char srtp_deinit (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char srtp_deinit (void); int main (void) { @@ -7136,12 +7338,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$as_ac_Lib=yes" -else $as_nop - eval "$as_ac_Lib=no" +else case e in #( + e) eval "$as_ac_Lib=no" ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi eval ac_res=\$$as_ac_Lib { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 @@ -7152,22 +7356,28 @@ then : fi if test "x$ac_srtp_deinit_present" != "x1"; then - as_ac_Lib=`printf "%s\n" "ac_cv_lib_$ac_external_srtp_lib""_srtp_shutdown" | $as_tr_sh` + as_ac_Lib=`printf "%s\n" "ac_cv_lib_$ac_external_srtp_lib""_srtp_shutdown" | sed "$as_sed_sh"` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for srtp_shutdown in -l$ac_external_srtp_lib" >&5 printf %s "checking for srtp_shutdown in -l$ac_external_srtp_lib... " >&6; } if eval test \${$as_ac_Lib+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-l$ac_external_srtp_lib $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char srtp_shutdown (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char srtp_shutdown (void); int main (void) { @@ -7179,12 +7389,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$as_ac_Lib=yes" -else $as_nop - eval "$as_ac_Lib=no" +else case e in #( + e) eval "$as_ac_Lib=no" ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi eval ac_res=\$$as_ac_Lib { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 @@ -7229,11 +7441,12 @@ then : printf "%s\n" "yes!!" >&6; } ac_external_yuv="1" -else $as_nop - +else case e in #( + e) as_fn_error $? "Unable to use external libyuv. If libyuv development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi @@ -7276,11 +7489,12 @@ then : printf "%s\n" "yes!!" >&6; } ac_external_webrtc="1" -else $as_nop - +else case e in #( + e) as_fn_error $? "Unable to use external webrtc. If webrtc development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi @@ -7322,11 +7536,12 @@ then : printf "%s\n" "yes!!" >&6; } ac_external_webrtc_aec3="1" -else $as_nop - +else case e in #( + e) as_fn_error $? "Unable to use external webrtc AEC3. If webrtc development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi @@ -7399,11 +7614,12 @@ then : printf "%s\n" "yes!!" >&6; } ac_external_pa="1" -else $as_nop - +else case e in #( + e) as_fn_error $? "Unable to use PortAudio. If PortAudio development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5 - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi @@ -7497,14 +7713,15 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - +else case e in #( + e) LIBS="$SAVED_LIBS" LDFLAGS="$SAVED_LDFLAGS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -7659,9 +7876,10 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_pjmedia_video_has_android=yes -else $as_nop - ac_pjmedia_video_has_android=no - +else case e in #( + e) ac_pjmedia_video_has_android=no + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -7713,9 +7931,10 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_pjmedia_video_has_darwin=yes -else $as_nop - ac_pjmedia_video_has_darwin=no - +else case e in #( + e) ac_pjmedia_video_has_darwin=no + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -7735,8 +7954,9 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_pjmedia_video_has_vtoolbox=yes -else $as_nop - ac_pjmedia_video_has_vtoolbox=no +else case e in #( + e) ac_pjmedia_video_has_vtoolbox=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -7756,8 +7976,9 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_pjmedia_video_has_metal=yes -else $as_nop - ac_pjmedia_video_has_metal=no +else case e in #( + e) ac_pjmedia_video_has_metal=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -7777,8 +7998,9 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_pjmedia_video_has_ios_opengl=yes -else $as_nop - ac_pjmedia_video_has_ios_opengl=no +else case e in #( + e) ac_pjmedia_video_has_ios_opengl=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -7845,8 +8067,9 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_pjmedia_video_has_qt=yes -else $as_nop - ac_pjmedia_video_has_qt=no +else case e in #( + e) ac_pjmedia_video_has_qt=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -7890,10 +8113,11 @@ then : printf "%s\n" "Checking if small filter is disabled... yes" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if small filter is disabled... no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if small filter is disabled... no" >&5 printf "%s\n" "Checking if small filter is disabled... no" >&6; } - + ;; +esac fi @@ -7908,10 +8132,11 @@ then : printf "%s\n" "Checking if large filter is disabled... yes" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if large filter is disabled... no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if large filter is disabled... no" >&5 printf "%s\n" "Checking if large filter is disabled... no" >&6; } - + ;; +esac fi @@ -7926,10 +8151,11 @@ then : printf "%s\n" "Checking if Speex AEC is disabled...yes" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex AEC is disabled...no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex AEC is disabled...no" >&5 printf "%s\n" "Checking if Speex AEC is disabled...no" >&6; } - + ;; +esac fi @@ -7946,10 +8172,11 @@ then : printf "%s\n" "Checking if G.711 codec is disabled...yes" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if G.711 codec is disabled...no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if G.711 codec is disabled...no" >&5 printf "%s\n" "Checking if G.711 codec is disabled...no" >&6; } - + ;; +esac fi @@ -7967,10 +8194,11 @@ then : printf "%s\n" "Checking if L16 codecs are disabled...yes" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if L16 codec is disabled...no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if L16 codec is disabled...no" >&5 printf "%s\n" "Checking if L16 codec is disabled...no" >&6; } - + ;; +esac fi @@ -7987,10 +8215,11 @@ then : printf "%s\n" "Checking if GSM codec is disabled...yes" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if GSM codec is disabled...no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if GSM codec is disabled...no" >&5 printf "%s\n" "Checking if GSM codec is disabled...no" >&6; } - + ;; +esac fi @@ -8007,10 +8236,11 @@ then : printf "%s\n" "Checking if G.722 codec is disabled...yes" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if G.722 codec is disabled...no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if G.722 codec is disabled...no" >&5 printf "%s\n" "Checking if G.722 codec is disabled...no" >&6; } - + ;; +esac fi @@ -8027,10 +8257,11 @@ then : printf "%s\n" "Checking if G.722.1 codec is disabled...yes" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if G.722.1 codec is disabled...no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if G.722.1 codec is disabled...no" >&5 printf "%s\n" "Checking if G.722.1 codec is disabled...no" >&6; } - + ;; +esac fi @@ -8047,10 +8278,11 @@ then : printf "%s\n" "Checking if Speex codec is disabled...yes" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex codec is disabled...no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex codec is disabled...no" >&5 printf "%s\n" "Checking if Speex codec is disabled...no" >&6; } - + ;; +esac fi @@ -8067,10 +8299,11 @@ then : printf "%s\n" "Checking if iLBC codec is disabled...yes" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if iLBC codec is disabled...no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if iLBC codec is disabled...no" >&5 printf "%s\n" "Checking if iLBC codec is disabled...no" >&6; } - + ;; +esac fi @@ -8086,16 +8319,22 @@ printf %s "checking for src_new in -lsamplerate... " >&6; } if test ${ac_cv_lib_samplerate_src_new+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsamplerate $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char src_new (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char src_new (void); int main (void) { @@ -8107,12 +8346,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_samplerate_src_new=yes -else $as_nop - ac_cv_lib_samplerate_src_new=no +else case e in #( + e) ac_cv_lib_samplerate_src_new=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_samplerate_src_new" >&5 printf "%s\n" "$ac_cv_lib_samplerate_src_new" >&6; } @@ -8130,10 +8371,11 @@ fi printf "%s\n" "Checking if libsamplerate is enabled...no" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libsamplerate is enabled...no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libsamplerate is enabled...no" >&5 printf "%s\n" "Checking if libsamplerate is enabled...no" >&6; } - + ;; +esac fi @@ -8148,10 +8390,11 @@ then : printf "%s\n" "Building libresample as shared library... yes" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Building libresample as shared library... no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Building libresample as shared library... no" >&5 printf "%s\n" "Building libresample as shared library... no" >&6; } - + ;; +esac fi @@ -8168,10 +8411,11 @@ printf "%s\n" "Checking if Speex resample is enabled... yes" >&6; } printf "%s\n" "Checking if Speex resample is enabled... no" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex resample is enabled... no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if Speex resample is enabled... no" >&5 printf "%s\n" "Checking if Speex resample is enabled... no" >&6; } - + ;; +esac fi @@ -8180,14 +8424,15 @@ fi if test ${with_sdl+y} then : withval=$with_sdl; -else $as_nop - with_sdl=no - +else case e in #( + e) with_sdl=no + ;; +esac fi if test "x$ac_cross_compile" != "x" -a "x$with_sdl" = "xno"; then - nable_sdl=no + enable_sdl=no fi # Check whether --enable-sdl was given. @@ -8199,8 +8444,8 @@ then : printf "%s\n" "Checking if SDL is disabled... yes" >&6; } fi -else $as_nop - +else case e in #( + e) if test "x$with_sdl" != "xyes" -a "x$with_sdl" != "xno" -a "x$with_sdl" != "x"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using SDL prefix... $with_sdl" >&5 printf "%s\n" "Using SDL prefix... $with_sdl" >&6; } @@ -8213,8 +8458,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_SDL_CONFIG+y} then : printf %s "(cached) " >&6 -else $as_nop - case $SDL_CONFIG in +else case e in #( + e) case $SDL_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_SDL_CONFIG="$SDL_CONFIG" # Let the user override the test with a path. ;; @@ -8239,6 +8484,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi SDL_CONFIG=$ac_cv_path_SDL_CONFIG @@ -8264,8 +8510,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_SDL_CONFIG+y} then : printf %s "(cached) " >&6 -else $as_nop - case $SDL_CONFIG in +else case e in #( + e) case $SDL_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_SDL_CONFIG="$SDL_CONFIG" # Let the user override the test with a path. ;; @@ -8290,6 +8536,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi SDL_CONFIG=$ac_cv_path_SDL_CONFIG @@ -8325,7 +8572,8 @@ printf "%s\n" "not found" >&6; } printf "%s\n" "Unsupported SDL version" >&6; } fi - + ;; +esac fi @@ -8334,9 +8582,10 @@ fi if test ${with_ffmpeg+y} then : withval=$with_ffmpeg; -else $as_nop - with_ffmpeg=no - +else case e in #( + e) with_ffmpeg=no + ;; +esac fi @@ -8355,8 +8604,8 @@ then : printf "%s\n" "Checking if ffmpeg is disabled... yes" >&6; } fi -else $as_nop - +else case e in #( + e) @@ -8420,16 +8669,22 @@ printf %s "checking for avdevice_version in -lavdevice... " >&6; } if test ${ac_cv_lib_avdevice_avdevice_version+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lavdevice $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char avdevice_version (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char avdevice_version (void); int main (void) { @@ -8441,12 +8696,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_avdevice_avdevice_version=yes -else $as_nop - ac_cv_lib_avdevice_avdevice_version=no +else case e in #( + e) ac_cv_lib_avdevice_avdevice_version=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avdevice_avdevice_version" >&5 printf "%s\n" "$ac_cv_lib_avdevice_avdevice_version" >&6; } @@ -8463,16 +8720,22 @@ printf %s "checking for av_malloc in -lavutil... " >&6; } if test ${ac_cv_lib_avutil_av_malloc+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lavutil $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char av_malloc (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char av_malloc (void); int main (void) { @@ -8484,12 +8747,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_avutil_av_malloc=yes -else $as_nop - ac_cv_lib_avutil_av_malloc=no +else case e in #( + e) ac_cv_lib_avutil_av_malloc=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avutil_av_malloc" >&5 printf "%s\n" "$ac_cv_lib_avutil_av_malloc" >&6; } @@ -8506,16 +8771,22 @@ printf %s "checking for avcodec_init in -lavcodec... " >&6; } if test ${ac_cv_lib_avcodec_avcodec_init+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lavcodec -lavutil $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char avcodec_init (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char avcodec_init (void); int main (void) { @@ -8527,12 +8798,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_avcodec_avcodec_init=yes -else $as_nop - ac_cv_lib_avcodec_avcodec_init=no +else case e in #( + e) ac_cv_lib_avcodec_avcodec_init=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avcodec_avcodec_init" >&5 printf "%s\n" "$ac_cv_lib_avcodec_avcodec_init" >&6; } @@ -8549,16 +8822,22 @@ printf %s "checking for av_register_all in -lavformat... " >&6; } if test ${ac_cv_lib_avformat_av_register_all+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lavformat -lavcodec -lavutil $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char av_register_all (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char av_register_all (void); int main (void) { @@ -8570,12 +8849,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_avformat_av_register_all=yes -else $as_nop - ac_cv_lib_avformat_av_register_all=no +else case e in #( + e) ac_cv_lib_avformat_av_register_all=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avformat_av_register_all" >&5 printf "%s\n" "$ac_cv_lib_avformat_av_register_all" >&6; } @@ -8592,16 +8873,22 @@ printf %s "checking for sws_scale in -lswscale... " >&6; } if test ${ac_cv_lib_swscale_sws_scale+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lswscale -lavutil $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sws_scale (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char sws_scale (void); int main (void) { @@ -8613,12 +8900,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_swscale_sws_scale=yes -else $as_nop - ac_cv_lib_swscale_sws_scale=no +else case e in #( + e) ac_cv_lib_swscale_sws_scale=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_swscale_sws_scale" >&5 printf "%s\n" "$ac_cv_lib_swscale_sws_scale" >&6; } @@ -8635,16 +8924,22 @@ printf %s "checking for avcore_version in -lavcore... " >&6; } if test ${ac_cv_lib_avcore_avcore_version+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lavcore $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char avcore_version (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char avcore_version (void); int main (void) { @@ -8656,12 +8951,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_avcore_avcore_version=yes -else $as_nop - ac_cv_lib_avcore_avcore_version=no +else case e in #( + e) ac_cv_lib_avcore_avcore_version=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avcore_avcore_version" >&5 printf "%s\n" "$ac_cv_lib_avcore_avcore_version" >&6; } @@ -8682,16 +8979,18 @@ then : printf "%s\n" "#define HAVE_ENUM_AVPIXELFORMAT 1" >>confdefs.h -else $as_nop - - ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_USE_OLD_FFMPEG=1" +else case e in #( + e) + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_USE_OLD_FFMPEG=1" ;; +esac fi LIBS="$LIBS $ac_ffmpeg_ldflags" export PKG_CONFIG_PATH=$SAVED_PKG_CONFIG_PATH - + ;; +esac fi @@ -8704,8 +9003,8 @@ then : printf "%s\n" "Checking if V4L2 is disabled... yes" >&6; } fi -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for v4l2_open in -lv4l2" >&5 @@ -8713,16 +9012,22 @@ printf %s "checking for v4l2_open in -lv4l2... " >&6; } if test ${ac_cv_lib_v4l2_v4l2_open+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lv4l2 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char v4l2_open (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char v4l2_open (void); int main (void) { @@ -8734,12 +9039,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_v4l2_v4l2_open=yes -else $as_nop - ac_cv_lib_v4l2_v4l2_open=no +else case e in #( + e) ac_cv_lib_v4l2_v4l2_open=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_v4l2_v4l2_open" >&5 printf "%s\n" "$ac_cv_lib_v4l2_v4l2_open" >&6; } @@ -8752,7 +9059,8 @@ then : fi - + ;; +esac fi @@ -8761,9 +9069,10 @@ fi if test ${with_openh264+y} then : withval=$with_openh264; -else $as_nop - with_openh264=no - +else case e in #( + e) with_openh264=no + ;; +esac fi @@ -8782,8 +9091,8 @@ then : printf "%s\n" "Checking if OpenH264 is disabled... yes" >&6; } fi -else $as_nop - +else case e in #( + e) if test "x$with_openh264" != "xno" -a "x$with_openh264" != "x"; then OPENH264_PREFIX=$with_openh264 OPENH264_CFLAGS="-I$OPENH264_PREFIX/include" @@ -8831,20 +9140,22 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - +else case e in #( + e) LIBS="$SAVED_LIBS" LDFLAGS="$SAVED_LDFLAGS" CFLAGS="$SAVED_CFLAGS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi @@ -8853,9 +9164,10 @@ fi if test ${with_vpx+y} then : withval=$with_vpx; -else $as_nop - with_vpx=no - +else case e in #( + e) with_vpx=no + ;; +esac fi @@ -8874,8 +9186,8 @@ then : printf "%s\n" "Checking if VPX is disabled... yes" >&6; } fi -else $as_nop - +else case e in #( + e) if test "x$with_vpx" != "xno" -a "x$with_vpx" != "x"; then VPX_PREFIX=$with_vpx VPX_CFLAGS="-I$VPX_PREFIX/include" @@ -8923,20 +9235,22 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - +else case e in #( + e) LIBS="$SAVED_LIBS" LDFLAGS="$SAVED_LDFLAGS" CFLAGS="$SAVED_CFLAGS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi @@ -8944,9 +9258,10 @@ fi if test ${enable_ipp+y} then : enableval=$enable_ipp; -else $as_nop - enable_ipp=no - +else case e in #( + e) enable_ipp=no + ;; +esac fi @@ -8955,9 +9270,10 @@ fi if test ${with_ipp+y} then : withval=$with_ipp; -else $as_nop - with_ipp=no - +else case e in #( + e) with_ipp=no + ;; +esac fi @@ -8966,9 +9282,10 @@ fi if test ${with_ipp_samples+y} then : withval=$with_ipp_samples; -else $as_nop - with_ipp_samples=no - +else case e in #( + e) with_ipp_samples=no + ;; +esac fi @@ -8977,9 +9294,10 @@ fi if test ${with_ipp_arch+y} then : withval=$with_ipp_arch; -else $as_nop - with_ipp_arch=no - +else case e in #( + e) with_ipp_arch=no + ;; +esac fi @@ -9090,12 +9408,13 @@ if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "Error: unable to recognize your IPP installation. Make sure the paths and ARCH suffix are set correctly, run with --help for more info -See \`config.log' for more details" "$LINENO" 5; } - +See 'config.log' for more details" "$LINENO" 5; } + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -9148,24 +9467,24 @@ printf %s "checking Intel IPP USC build location... " >&6; } # icc compiler IPPSAMP_DIR=`ls -d $IPPSAMPLES/speech-codecs/_bin/*icc*/lib | head -1` else - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "Unable to find to find built binaries under $IPPSAMPLES/speech-codecs/{bin,_bin}. Have you built the IPP samples? -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } fi else - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "unable to find $IPPSAMPLES/speech-codecs/bin/*gcc*/lib or $IPPSAMPLES/speech-codecs/_bin/*gcc*/lib directory. Have you built the samples? -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } fi # Test the directory if test ! -d $IPPSAMP_DIR; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "There's something wrong with this script, directory $IPPSAMP_DIR does not exist -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } exit 1; fi @@ -9176,17 +9495,17 @@ See \`config.log' for more details" "$LINENO" 5; } IPPSAMP_LIBS="libspeech.a" IPPSAMP_LDLIBS="-lspeech" else - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "bug in this script: unsupported IPP version -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } fi if test ! -f $IPPSAMP_DIR/$IPPSAMP_LIBS; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "$IPPSAMP_LIBS doesn't exist in $IPPSAMP_DIR -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $IPPSAMP_DIR" >&5 @@ -9221,12 +9540,13 @@ if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "no -See \`config.log' for more details" "$LINENO" 5; } - +See 'config.log' for more details" "$LINENO" 5; } + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -9265,8 +9585,8 @@ then : printf "%s\n" "Checking if Android MediaCodec support is disabled... yes" >&6; } fi -else $as_nop - +else case e in #( + e) case $target in *android*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for AMediaCodec_createDecoderByType in -lmediandk" >&5 @@ -9274,16 +9594,22 @@ printf %s "checking for AMediaCodec_createDecoderByType in -lmediandk... " >&6; if test ${ac_cv_lib_mediandk_AMediaCodec_createDecoderByType+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lmediandk $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char AMediaCodec_createDecoderByType (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char AMediaCodec_createDecoderByType (void); int main (void) { @@ -9295,12 +9621,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_mediandk_AMediaCodec_createDecoderByType=yes -else $as_nop - ac_cv_lib_mediandk_AMediaCodec_createDecoderByType=no +else case e in #( + e) ac_cv_lib_mediandk_AMediaCodec_createDecoderByType=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_mediandk_AMediaCodec_createDecoderByType" >&5 printf "%s\n" "$ac_cv_lib_mediandk_AMediaCodec_createDecoderByType" >&6; } @@ -9323,7 +9651,8 @@ printf "%s\n" "Checking if Android AMediaCodec library is available... no" >&6; ;; esac - + ;; +esac fi @@ -9333,9 +9662,10 @@ fi if test ${with_ssl+y} then : withval=$with_ssl; -else $as_nop - with_ssl=no - +else case e in #( + e) with_ssl=no + ;; +esac fi @@ -9344,9 +9674,10 @@ fi if test ${with_gnutls+y} then : withval=$with_gnutls; -else $as_nop - with_gnutls=no - +else case e in #( + e) with_gnutls=no + ;; +esac fi @@ -9368,8 +9699,8 @@ then : printf "%s\n" "Checking if Darwin SSL support is disabled... yes" >&6; } fi -else $as_nop - +else case e in #( + e) case $target in *darwin*) SAVED_CFLAGS="$CFLAGS" @@ -9417,7 +9748,8 @@ printf "%s\n" "Checking if Darwin SSL is available... no" >&6; } ;; esac - + ;; +esac fi @@ -9431,8 +9763,8 @@ then : printf "%s\n" "Checking if SSL support is disabled... yes" >&6; } fi -else $as_nop - +else case e in #( + e) if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then CFLAGS="$CFLAGS -I$with_ssl/include" CPPFLAGS="$CPPFLAGS -I$with_ssl/include" @@ -9461,16 +9793,22 @@ printf %s "checking for ERR_load_BIO_strings in -lcrypto... " >&6; } if test ${ac_cv_lib_crypto_ERR_load_BIO_strings+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypto $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char ERR_load_BIO_strings (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char ERR_load_BIO_strings (void); int main (void) { @@ -9482,12 +9820,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_crypto_ERR_load_BIO_strings=yes -else $as_nop - ac_cv_lib_crypto_ERR_load_BIO_strings=no +else case e in #( + e) ac_cv_lib_crypto_ERR_load_BIO_strings=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_ERR_load_BIO_strings" >&5 printf "%s\n" "$ac_cv_lib_crypto_ERR_load_BIO_strings" >&6; } @@ -9495,23 +9835,29 @@ if test "x$ac_cv_lib_crypto_ERR_load_BIO_strings" = xyes then : libcrypto_present=1 && LIBS="-lcrypto $LIBS" -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ERR_get_error in -lcrypto" >&5 printf %s "checking for ERR_get_error in -lcrypto... " >&6; } if test ${ac_cv_lib_crypto_ERR_get_error+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypto $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char ERR_get_error (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char ERR_get_error (void); int main (void) { @@ -9523,12 +9869,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_crypto_ERR_get_error=yes -else $as_nop - ac_cv_lib_crypto_ERR_get_error=no +else case e in #( + e) ac_cv_lib_crypto_ERR_get_error=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_ERR_get_error" >&5 printf "%s\n" "$ac_cv_lib_crypto_ERR_get_error" >&6; } @@ -9538,7 +9886,8 @@ then : fi - + ;; +esac fi @@ -9547,16 +9896,22 @@ printf %s "checking for SSL_CTX_new in -lssl... " >&6; } if test ${ac_cv_lib_ssl_SSL_CTX_new+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char SSL_CTX_new (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char SSL_CTX_new (void); int main (void) { @@ -9568,12 +9923,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ssl_SSL_CTX_new=yes -else $as_nop - ac_cv_lib_ssl_SSL_CTX_new=no +else case e in #( + e) ac_cv_lib_ssl_SSL_CTX_new=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_SSL_CTX_new" >&5 printf "%s\n" "$ac_cv_lib_ssl_SSL_CTX_new" >&6; } @@ -9615,16 +9972,22 @@ printf %s "checking for EVP_aes_128_gcm in -lcrypto... " >&6; } if test ${ac_cv_lib_crypto_EVP_aes_128_gcm+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypto $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char EVP_aes_128_gcm (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char EVP_aes_128_gcm (void); int main (void) { @@ -9636,12 +9999,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_crypto_EVP_aes_128_gcm=yes -else $as_nop - ac_cv_lib_crypto_EVP_aes_128_gcm=no +else case e in #( + e) ac_cv_lib_crypto_EVP_aes_128_gcm=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_EVP_aes_128_gcm" >&5 printf "%s\n" "$ac_cv_lib_crypto_EVP_aes_128_gcm" >&6; } @@ -9707,16 +10072,22 @@ printf %s "checking for gnutls_certificate_set_x509_system_trust in -lgnutls... if test ${ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lgnutls $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char gnutls_certificate_set_x509_system_trust (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char gnutls_certificate_set_x509_system_trust (void); int main (void) { @@ -9728,12 +10099,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust=yes -else $as_nop - ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust=no +else case e in #( + e) ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust" >&5 printf "%s\n" "$ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust" >&6; } @@ -9764,7 +10137,8 @@ printf "%s\n" "** No GnuTLS libraries found, disabling SSL support **" >&6; } fi - + ;; +esac fi @@ -9773,9 +10147,10 @@ fi if test ${with_opencore_amrnb+y} then : withval=$with_opencore_amrnb; as_fn_error $? "This option is obsolete and replaced by --with-opencore-amr=DIR" "$LINENO" 5 -else $as_nop - true; - +else case e in #( + e) true; + ;; +esac fi @@ -9784,9 +10159,10 @@ fi if test ${with_opencore_amr+y} then : withval=$with_opencore_amr; -else $as_nop - with_opencore_amr=no - +else case e in #( + e) with_opencore_amr=no + ;; +esac fi @@ -9799,9 +10175,10 @@ fi if test ${with_opencore_amrwbenc+y} then : withval=$with_opencore_amrwbenc; -else $as_nop - with_opencore_amrwbenc=no - +else case e in #( + e) with_opencore_amrwbenc=no + ;; +esac fi @@ -9827,8 +10204,8 @@ then : printf "%s\n" "Checking if OpenCORE AMR support is disabled... yes" >&6; } fi -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: checking for OpenCORE AMR installations.." >&5 printf "%s\n" "checking for OpenCORE AMR installations.." >&6; } if test "x$with_opencore_amr" != "xno" -a "x$with_opencore_amr" != "x"; then @@ -9860,16 +10237,22 @@ printf %s "checking for Encoder_Interface_init in -lopencore-amrnb... " >&6; } if test ${ac_cv_lib_opencore_amrnb_Encoder_Interface_init+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lopencore-amrnb $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char Encoder_Interface_init (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char Encoder_Interface_init (void); int main (void) { @@ -9881,12 +10264,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_opencore_amrnb_Encoder_Interface_init=yes -else $as_nop - ac_cv_lib_opencore_amrnb_Encoder_Interface_init=no +else case e in #( + e) ac_cv_lib_opencore_amrnb_Encoder_Interface_init=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_opencore_amrnb_Encoder_Interface_init" >&5 printf "%s\n" "$ac_cv_lib_opencore_amrnb_Encoder_Interface_init" >&6; } @@ -9930,16 +10315,22 @@ printf %s "checking for D_IF_init in -lopencore-amrwb... " >&6; } if test ${ac_cv_lib_opencore_amrwb_D_IF_init+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lopencore-amrwb $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char D_IF_init (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char D_IF_init (void); int main (void) { @@ -9951,12 +10342,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_opencore_amrwb_D_IF_init=yes -else $as_nop - ac_cv_lib_opencore_amrwb_D_IF_init=no +else case e in #( + e) ac_cv_lib_opencore_amrwb_D_IF_init=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_opencore_amrwb_D_IF_init" >&5 printf "%s\n" "$ac_cv_lib_opencore_amrwb_D_IF_init" >&6; } @@ -9972,16 +10365,22 @@ printf %s "checking for E_IF_init in -lvo-amrwbenc... " >&6; } if test ${ac_cv_lib_vo_amrwbenc_E_IF_init+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lvo-amrwbenc $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char E_IF_init (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char E_IF_init (void); int main (void) { @@ -9993,12 +10392,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_vo_amrwbenc_E_IF_init=yes -else $as_nop - ac_cv_lib_vo_amrwbenc_E_IF_init=no +else case e in #( + e) ac_cv_lib_vo_amrwbenc_E_IF_init=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_vo_amrwbenc_E_IF_init" >&5 printf "%s\n" "$ac_cv_lib_vo_amrwbenc_E_IF_init" >&6; } @@ -10020,7 +10421,8 @@ printf "%s\n" "OpenCORE AMR-WB library found, AMR-WB support enabled" >&6; } fi - + ;; +esac fi @@ -10029,9 +10431,10 @@ fi if test ${with_silk+y} then : withval=$with_silk; -else $as_nop - with_silk=no - +else case e in #( + e) with_silk=no + ;; +esac fi @@ -10052,8 +10455,8 @@ then : printf "%s\n" "Checking if SILK support is disabled... yes" >&6; } fi -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: checking for SILK installations.." >&5 printf "%s\n" "checking for SILK installations.." >&6; } if test "x$with_silk" != "xno" -a "x$with_silk" != "x"; then @@ -10077,16 +10480,22 @@ printf %s "checking for SKP_Silk_SDK_get_version in -lSKP_SILK_SDK... " >&6; } if test ${ac_cv_lib_SKP_SILK_SDK_SKP_Silk_SDK_get_version+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lSKP_SILK_SDK $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char SKP_Silk_SDK_get_version (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char SKP_Silk_SDK_get_version (void); int main (void) { @@ -10098,12 +10507,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_SKP_SILK_SDK_SKP_Silk_SDK_get_version=yes -else $as_nop - ac_cv_lib_SKP_SILK_SDK_SKP_Silk_SDK_get_version=no +else case e in #( + e) ac_cv_lib_SKP_SILK_SDK_SKP_Silk_SDK_get_version=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_SKP_SILK_SDK_SKP_Silk_SDK_get_version" >&5 printf "%s\n" "$ac_cv_lib_SKP_SILK_SDK_SKP_Silk_SDK_get_version" >&6; } @@ -10125,7 +10536,8 @@ printf "%s\n" "SILK library found, SILK support enabled" >&6; } fi - + ;; +esac fi @@ -10138,9 +10550,10 @@ fi if test ${with_opus+y} then : withval=$with_opus; -else $as_nop - with_opus=no - +else case e in #( + e) with_opus=no + ;; +esac fi @@ -10157,8 +10570,8 @@ then : printf "%s\n" "Checking if OPUS support is disabled... yes" >&6; } fi -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: checking for OPUS installations.." >&5 printf "%s\n" "checking for OPUS installations.." >&6; } if test "x$with_opus" != "xno" -a "x$with_opus" != "x"; then @@ -10181,16 +10594,22 @@ printf %s "checking for opus_repacketizer_get_size in -lopus... " >&6; } if test ${ac_cv_lib_opus_opus_repacketizer_get_size+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lopus $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char opus_repacketizer_get_size (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char opus_repacketizer_get_size (void); int main (void) { @@ -10202,12 +10621,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_opus_opus_repacketizer_get_size=yes -else $as_nop - ac_cv_lib_opus_opus_repacketizer_get_size=no +else case e in #( + e) ac_cv_lib_opus_opus_repacketizer_get_size=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_opus_opus_repacketizer_get_size" >&5 printf "%s\n" "$ac_cv_lib_opus_opus_repacketizer_get_size" >&6; } @@ -10231,7 +10652,8 @@ printf "%s\n" "OPUS library not found, OPUS support disabled" >&6; } fi - + ;; +esac fi @@ -10240,9 +10662,10 @@ fi if test ${with_bcg729+y} then : withval=$with_bcg729; -else $as_nop - with_bcg729=no - +else case e in #( + e) with_bcg729=no + ;; +esac fi @@ -10263,8 +10686,8 @@ then : printf "%s\n" "Checking if bcg729 is disabled... yes" >&6; } fi -else $as_nop - +else case e in #( + e) if test "x$with_bcg729" != "xno" -a "x$with_bcg729" != "x"; then BCG729_PREFIX=$with_bcg729 BCG729_CFLAGS="-I$BCG729_PREFIX/include" @@ -10312,8 +10735,8 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - +else case e in #( + e) ac_no_bcg729=1 printf "%s\n" "#define PJMEDIA_HAS_BCG729 0" >>confdefs.h @@ -10323,12 +10746,14 @@ else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi @@ -10337,9 +10762,10 @@ fi if test ${with_lyra+y} then : withval=$with_lyra; -else $as_nop - with_lyra=no - +else case e in #( + e) with_lyra=no + ;; +esac fi @@ -10361,8 +10787,8 @@ then : printf "%s\n" "Checking if lyra is disabled... yes" >&6; } fi -else $as_nop - +else case e in #( + e) if test "x$with_lyra" != "xno" -a "x$with_lyra" != "x"; then LYRA_PREFIX=$with_lyra LYRA_CPPFLAGS="-DGLOG_DEPRECATED=__attribute__((deprecated)) -DGLOG_EXPORT=__attribute__((visibility(\"default\"))) -DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))" @@ -10421,8 +10847,8 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - +else case e in #( + e) ac_no_lyra_codec=1 LIBS="$SAVED_LIBS" LDFLAGS="$SAVED_LDFLAGS" @@ -10430,7 +10856,8 @@ else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -10442,7 +10869,8 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu CPPFLAGS="$SAVED_CPPFLAGS" - + ;; +esac fi @@ -10458,10 +10886,11 @@ then : printf "%s\n" "Checking if libsrtp is disabled...yes" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libsrtp is disabled...no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libsrtp is disabled...no" >&5 printf "%s\n" "Checking if libsrtp is disabled...no" >&6; } - + ;; +esac fi @@ -10478,10 +10907,11 @@ then : printf "%s\n" "Checking if libyuv is disabled...yes" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libyuv is disabled...no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libyuv is disabled...no" >&5 printf "%s\n" "Checking if libyuv is disabled...no" >&6; } - + ;; +esac fi @@ -10508,9 +10938,10 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_support_neon_ext=yes -else $as_nop - ax_cv_support_neon_ext=no - +else case e in #( + e) ax_cv_support_neon_ext=no + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS="$SAVED_CFLAGS" @@ -10531,8 +10962,8 @@ then : printf "%s\n" "Checking if libwebrtc is disabled...yes" >&6; } fi -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libwebrtc is disabled...no" >&5 printf "%s\n" "Checking if libwebrtc is disabled...no" >&6; } case $target in @@ -10613,7 +11044,8 @@ printf "%s\n" "Checking if libwebrtc is disabled...no" >&6; } ;; esac - + ;; +esac fi @@ -10660,8 +11092,8 @@ then : printf "%s\n" "yes" >&6; } LD="$CXX" -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CXXFLAGS="$SAVED_CXXFLAGS" @@ -10669,7 +11101,8 @@ printf "%s\n" "no" >&6; } printf "%s\n" "#define PJMEDIA_HAS_LIBWEBRTC_AEC3 0" >>confdefs.h - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -10767,15 +11200,16 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu printf "%s\n" "Checking if libwebrtc-aec3 is enabled...no" >&6; } fi -else $as_nop - +else case e in #( + e) ac_no_webrtc_aec3=1 printf "%s\n" "#define PJMEDIA_HAS_LIBWEBRTC_AEC3 0" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if libwebrtc-aec3 is enabled...no" >&5 printf "%s\n" "Checking if libwebrtc-aec3 is enabled...no" >&6; } - + ;; +esac fi @@ -10913,8 +11347,8 @@ cat >confcache <<\_ACEOF # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # -# `ac_cv_env_foo' variables (set or unset) will be overridden when -# loading this file, other *unset* `ac_cv_foo' will be assigned the +# 'ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* 'ac_cv_foo' will be assigned the # following values. _ACEOF @@ -10944,14 +11378,14 @@ printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) - # `set' does not quote correctly, so add quotes: double-quote + # 'set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) - # `set' quotes correctly as required by POSIX, so do not add quotes. + # 'set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | @@ -11042,7 +11476,6 @@ cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh -as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh @@ -11051,12 +11484,13 @@ then : # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST -else $as_nop - case `(set -o) 2>/dev/null` in #( +else case e in #( + e) case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; +esac ;; esac fi @@ -11128,7 +11562,7 @@ IFS=$as_save_IFS ;; esac -# We did not find ourselves, most probably we were run as `sh COMMAND' +# We did not find ourselves, most probably we were run as 'sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 @@ -11157,7 +11591,6 @@ as_fn_error () } # as_fn_error - # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. @@ -11197,11 +11630,12 @@ then : { eval $1+=\$2 }' -else $as_nop - as_fn_append () +else case e in #( + e) as_fn_append () { eval $1=\$$1\$2 - } + } ;; +esac fi # as_fn_append # as_fn_arith ARG... @@ -11215,11 +11649,12 @@ then : { as_val=$(( $* )) }' -else $as_nop - as_fn_arith () +else case e in #( + e) as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` - } + } ;; +esac fi # as_fn_arith @@ -11302,9 +11737,9 @@ if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. + # 1) On MSYS, both 'ln -s file dir' and 'ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; 'ln -s' creates a wrapper executable. + # In both cases, we have to default to 'cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then @@ -11385,10 +11820,12 @@ as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" +as_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" +as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated # Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" +as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g" +as_tr_sh="eval sed '$as_sed_sh'" # deprecated exec 6>&1 @@ -11404,7 +11841,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # values after options handling. ac_log=" This file was extended by pjproject $as_me 2.x, which was -generated by GNU Autoconf 2.71. Invocation command line was +generated by GNU Autoconf 2.72. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -11435,7 +11872,7 @@ _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ -\`$as_me' instantiates files and other configuration actions +'$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. @@ -11468,10 +11905,10 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ pjproject config.status 2.x -configured by $0, generated by GNU Autoconf 2.71, +configured by $0, generated by GNU Autoconf 2.72, with options \\"\$ac_cs_config\\" -Copyright (C) 2021 Free Software Foundation, Inc. +Copyright (C) 2023 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -11530,8 +11967,8 @@ do ac_need_defaults=false;; --he | --h) # Conflict between --help and --header - as_fn_error $? "ambiguous option: \`$1' -Try \`$0 --help' for more information.";; + as_fn_error $? "ambiguous option: '$1' +Try '$0 --help' for more information.";; --help | --hel | -h ) printf "%s\n" "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ @@ -11539,8 +11976,8 @@ Try \`$0 --help' for more information.";; ac_cs_silent=: ;; # This is an error. - -*) as_fn_error $? "unrecognized option: \`$1' -Try \`$0 --help' for more information." ;; + -*) as_fn_error $? "unrecognized option: '$1' +Try '$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; @@ -11602,7 +12039,7 @@ do "pjsip/build/os-auto.mak") CONFIG_FILES="$CONFIG_FILES pjsip/build/os-auto.mak" ;; "third_party/build/os-auto.mak") CONFIG_FILES="$CONFIG_FILES third_party/build/os-auto.mak" ;; - *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + *) as_fn_error $? "invalid argument: '$ac_config_target'" "$LINENO" 5;; esac done @@ -11621,7 +12058,7 @@ fi # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: -# after its creation but before its name has been assigned to `$tmp'. +# after its creation but before its name has been assigned to '$tmp'. $debug || { tmp= ac_tmp= @@ -11645,7 +12082,7 @@ ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. -# This happens for instance with `./config.status config.h'. +# This happens for instance with './config.status config.h'. if test -n "$CONFIG_FILES"; then @@ -11803,13 +12240,13 @@ fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. -# This happens for instance with `./config.status Makefile'. +# This happens for instance with './config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF -# Transform confdefs.h into an awk script `defines.awk', embedded as +# Transform confdefs.h into an awk script 'defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. @@ -11919,7 +12356,7 @@ do esac case $ac_mode$ac_tag in :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :L* | :C*:*) as_fn_error $? "invalid tag '$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac @@ -11941,19 +12378,19 @@ do -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, - # because $ac_f cannot contain `:'. + # because $ac_f cannot contain ':'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || - as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + as_fn_error 1 "cannot find input file: '$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done - # Let's still pretend it is `configure' which instantiates (i.e., don't + # Let's still pretend it is 'configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` @@ -12077,7 +12514,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 esac _ACEOF -# Neutralize VPATH when `$srcdir' = `.'. +# Neutralize VPATH when '$srcdir' = '.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 @@ -12106,9 +12543,9 @@ test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable 'datarootdir' which seems to be undefined. Please make sure it is defined" >&5 -printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable 'datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" diff --git a/aconfigure.ac b/aconfigure.ac index 9095dbf2ce..d49cb18ea8 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -1517,7 +1517,7 @@ AC_ARG_WITH(sdl, dnl # Do not use default SDL installation if we are cross-compiling if test "x$ac_cross_compile" != "x" -a "x$with_sdl" = "xno"; then - nable_sdl=no + enable_sdl=no fi dnl # SDL From 557fb06da5cfc8688c19cd14ab37075d1fa64541 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 28 Nov 2024 12:02:34 +0700 Subject: [PATCH 119/491] Fix transport iteration bug in transport manager (#4179) --- pjsip/src/pjsip/sip_transport.c | 57 ++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index b56fb3e295..c5885f3056 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -52,8 +52,14 @@ static const char *addr_string(const pj_sockaddr_t *addr) static const char* print_tpsel_info(const pjsip_tpselector *sel) { static char tpsel_info_buf[80]; - if (!sel) return "(null)"; - if (sel->type==PJSIP_TPSELECTOR_LISTENER) + + if (!sel) + return "(null)"; + + if (sel->type==PJSIP_TPSELECTOR_NONE) + pj_ansi_snprintf(tpsel_info_buf, sizeof(tpsel_info_buf), + "none, reuse=%d", !sel->disable_connection_reuse); + else if (sel->type==PJSIP_TPSELECTOR_LISTENER) pj_ansi_snprintf(tpsel_info_buf, sizeof(tpsel_info_buf), "listener[%s], reuse=%d", sel->u.listener->obj_name, !sel->disable_connection_reuse); @@ -61,9 +67,13 @@ static const char* print_tpsel_info(const pjsip_tpselector *sel) pj_ansi_snprintf(tpsel_info_buf, sizeof(tpsel_info_buf), "transport[%s], reuse=%d", sel->u.transport->info, !sel->disable_connection_reuse); + else if (sel->type==PJSIP_TPSELECTOR_IP_VER) + pj_ansi_snprintf(tpsel_info_buf, sizeof(tpsel_info_buf), + "ip_ver[%d], reuse=%d", sel->u.ip_ver, + !sel->disable_connection_reuse); else pj_ansi_snprintf(tpsel_info_buf, sizeof(tpsel_info_buf), - "unknown[%p], reuse=%d", sel->u.ptr, + "unknown %d [%p], reuse=%d", sel->type, sel->u.ptr, !sel->disable_connection_reuse); return tpsel_info_buf; } @@ -2436,14 +2446,19 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, pjsip_tpfactory *factory; pj_status_t status; - TRACE_((THIS_FILE,"Acquiring transport type=%s, sel=%s remote=%s:%d", + TRACE_((THIS_FILE, "Acquiring transport type=%s, sel=%s remote=%s:%d " + "(%.*s)", pjsip_transport_get_type_name(type), print_tpsel_info(sel), addr_string(remote), - pj_sockaddr_get_port(remote))); + pj_sockaddr_get_port(remote), + tdata? tdata->dest_info.name.slen : 10, + tdata? tdata->dest_info.name.ptr : "-no tdata-")); pj_lock_acquire(mgr->lock); + TRACE_((THIS_FILE, "Acquiring transport got the lock")); + /* If transport is specified, then just use it if it is suitable * for the destination. */ @@ -2522,6 +2537,8 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, pj_bzero(&key, sizeof(key)); key_len = sizeof(key.type) + addr_len; + TRACE_((THIS_FILE, "Search transport by remote address")); + /* First try to get exact destination. */ key.type = type; pj_memcpy(&key.rem_addr, remote, addr_len); @@ -2530,6 +2547,10 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, NULL); if (tp_entry) { transport *tp_iter = tp_entry; + + TRACE_((THIS_FILE, "Found one, checking further (e.g: " + "destroying, verify hostname, etc)..")); + do { /* Don't use transport being shutdown/destroyed */ if (!tp_iter->tp->is_shutdown && @@ -2543,6 +2564,8 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, if (pj_stricmp(&tdata->dest_info.name, &tp_iter->tp->remote_name.host)) { + TRACE_((THIS_FILE, "Skipping secure transport " + "with different hostname")); tp_iter = tp_iter->next; continue; } @@ -2556,6 +2579,8 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, tp_ref = tp_iter->tp; break; } + TRACE_((THIS_FILE, "Skipping transport " + "with different listener")); } else { tp_ref = tp_iter->tp; break; @@ -2563,6 +2588,9 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, } tp_iter = tp_iter->next; } while (tp_iter != tp_entry); + + TRACE_((THIS_FILE, "Search by remote address found %s", + tp_ref? "one" : "none")); } } @@ -2571,6 +2599,8 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, { const pj_sockaddr *remote_addr = (const pj_sockaddr*)remote; + TRACE_((THIS_FILE, "Search loop & datagram transports " + "with address zero")); /* Ignore address for loop transports. */ if (type == PJSIP_TRANSPORT_LOOP || @@ -2598,14 +2628,19 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, key_len = sizeof(key.type) + addr_len; tp_entry = (transport *) pj_hash_get(mgr->table, &key, key_len, NULL); - - while (tp_entry) { - tp_ref = tp_entry->tp; - if (!tp_ref->is_shutdown && !tp_ref->is_destroying) - break; - tp_entry = tp_entry->next; + if (tp_entry) { + transport *tp_iter = tp_entry; + do { + tp_ref = tp_iter->tp; + if (!tp_ref->is_shutdown && !tp_ref->is_destroying) + break; + tp_iter = tp_iter->next; + } while (tp_iter != tp_entry); } } + + TRACE_((THIS_FILE, "Search loop & datagram transports found %s", + tp_ref? "one" : "none")); } /* If transport is found and listener is specified, verify listener */ From bdc3357cbb0042d2a064555fd0077c95475eda3f Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 28 Nov 2024 12:03:11 +0700 Subject: [PATCH 120/491] Fix misc warnings of pointer-integer conversion on MSVC x64 (#4176) --- pjlib/include/pj/unittest.h | 7 ++++--- pjlib/src/pj/unittest.c | 3 ++- pjlib/src/pjlib-test/unittest_test.c | 16 ++++++++-------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/pjlib/include/pj/unittest.h b/pjlib/include/pj/unittest.h index c11b252ee5..177060d19d 100644 --- a/pjlib/include/pj/unittest.h +++ b/pjlib/include/pj/unittest.h @@ -74,8 +74,8 @@ PJ_BEGIN_DECL */ #define PJ_TEST_BINARY_OP(expr0, op, expr1, err_reason, err_action) \ { \ - long tmp_value0_ = (long)(expr0); \ - long tmp_value1_ = (long)(expr1); \ + intptr_t tmp_value0_ = (intptr_t)(expr0); \ + intptr_t tmp_value1_ = (intptr_t)(expr1); \ if (!(tmp_value0_ op tmp_value1_)) { \ const char *tmp_reason_ = err_reason; \ const char *sep0_ = (tmp_reason_ ? " (": ""); \ @@ -83,7 +83,8 @@ PJ_BEGIN_DECL if (!tmp_reason_) tmp_reason_=""; \ PJ_LOG(1,(THIS_FILE, "Test \"%s\" (value=%ld) " #op \ " \"%s\" (value=%ld) fails in %s:%d%s%s%s", \ - #expr0, tmp_value0_, #expr1, tmp_value1_, \ + #expr0, (long)tmp_value0_, \ + #expr1, (long)tmp_value1_, \ THIS_FILE, __LINE__, \ sep0_, tmp_reason_, sep1_)); \ err_action; \ diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index 5b7cadede1..428051b520 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -265,7 +265,8 @@ static const char *get_test_case_info(const pj_test_case *tc, } else { char arg_val[40]; /* treat argument as integer */ - pj_ansi_snprintf(arg_val, sizeof(arg_val), "%ld", (long)tc->arg); + pj_ansi_snprintf(arg_val, sizeof(arg_val), "%ld", + (long)(intptr_t)tc->arg); /* if arg value is too long (e.g. it's a pointer!), then just show * a portion of it */ diff --git a/pjlib/src/pjlib-test/unittest_test.c b/pjlib/src/pjlib-test/unittest_test.c index 980e1990fc..dd3e413c99 100644 --- a/pjlib/src/pjlib-test/unittest_test.c +++ b/pjlib/src/pjlib-test/unittest_test.c @@ -237,7 +237,7 @@ enum test_flags /** Dummy test */ static int func_to_test(void *arg) { - unsigned flags = (unsigned)(long)arg; + unsigned flags = (unsigned)(intptr_t)arg; unsigned test_id = (flags & 31); /* Note that for simplicity, make the length of log messages the same @@ -452,7 +452,7 @@ static int shuffle_test() for (i=0; iarg, 2, seed_info, return -40); + PJ_TEST_EQ(tc->arg, 2, seed_info, return -40); tc = tc->next; - PJ_TEST_EQ((long)tc->arg, 5, seed_info, return -41); + PJ_TEST_EQ(tc->arg, 5, seed_info, return -41); tc = tc->next; - PJ_TEST_TRUE((long)tc->arg==0 || (long)tc->arg==3, seed_info, + PJ_TEST_TRUE(tc->arg==0 || (intptr_t)tc->arg==3, seed_info, return -42); tc = tc->next; - PJ_TEST_TRUE((long)tc->arg==0 || (long)tc->arg==3, seed_info, + PJ_TEST_TRUE(tc->arg==0 || (intptr_t)tc->arg==3, seed_info, return -43); tc = tc->next; - PJ_TEST_EQ((long)tc->arg, 1, seed_info, return -44); + PJ_TEST_EQ(tc->arg, 1, seed_info, return -44); tc = tc->next; - PJ_TEST_EQ((long)tc->arg, 4, seed_info, return -45); + PJ_TEST_EQ(tc->arg, 4, seed_info, return -45); } return 0; From 024d019752605872d7f549f9395bf4976b6d93f2 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 28 Nov 2024 17:21:37 +0700 Subject: [PATCH 121/491] Update TCP & TLS listener destroy logs. (#4186) --- pjsip/src/pjsip/sip_transport_tcp.c | 2 +- pjsip/src/pjsip/sip_transport_tls.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c index 973ecfb0ea..51e847562a 100644 --- a/pjsip/src/pjsip/sip_transport_tcp.c +++ b/pjsip/src/pjsip/sip_transport_tcp.c @@ -520,7 +520,7 @@ static void lis_on_destroy(void *arg) } if (listener->factory.pool) { - PJ_LOG(4,(listener->factory.obj_name, "SIP TCP transport destroyed")); + PJ_LOG(4,(listener->factory.obj_name, "SIP TCP listener destroyed")); pj_pool_safe_release(&listener->factory.pool); } } diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c index 6a724e3d4e..037615d96a 100644 --- a/pjsip/src/pjsip/sip_transport_tls.c +++ b/pjsip/src/pjsip/sip_transport_tls.c @@ -719,7 +719,7 @@ static void lis_on_destroy(void *arg) } if (listener->factory.pool) { - PJ_LOG(4,(listener->factory.obj_name, "SIP TLS transport destroyed")); + PJ_LOG(4,(listener->factory.obj_name, "SIP TLS listener destroyed")); pj_pool_secure_release(&listener->factory.pool); } } From af4c828ec6746c5c42b4bf6faf915175dbbb7b42 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Fri, 29 Nov 2024 16:18:34 +0700 Subject: [PATCH 122/491] Fix stream lingering issues due to using transport's group lock (#4184) --- pjmedia/src/pjmedia/stream.c | 32 +++++++++++++++++++++++++------- pjmedia/src/pjmedia/vid_stream.c | 13 +++++++++++++ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index f2cd3ecc5a..bbecb20be0 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -2004,7 +2004,11 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) } } + /* Add ref counter to avoid premature destroy from callbacks */ + pj_grp_lock_add_ref(stream->grp_lock); + pj_bzero(&seq_st, sizeof(seq_st)); + /* Ignore the packet if decoder is paused */ if (channel->paused) { goto on_return; @@ -2319,6 +2323,8 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) stream->initial_rr = PJ_TRUE; } } + + pj_grp_lock_dec_ref(stream->grp_lock); } @@ -2933,13 +2939,16 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, att_param.rtp_cb2 = &on_rx_rtp; att_param.rtcp_cb = &on_rx_rtcp; - /* Attach handler to group lock from transport */ - if (tp->grp_lock) { - stream->grp_lock = stream->port.grp_lock = tp->grp_lock; - pj_grp_lock_add_ref(stream->grp_lock); - pj_grp_lock_add_handler(stream->grp_lock, pool, stream, - &stream_on_destroy); - } + /* Create group lock & attach handler */ + status = pj_grp_lock_create_w_handler(pool, NULL, stream, + &stream_on_destroy, + &stream->grp_lock); + if (status != PJ_SUCCESS) + goto err_cleanup; + + /* Add ref */ + pj_grp_lock_add_ref(stream->grp_lock); + stream->port.grp_lock = stream->grp_lock; /* Only attach transport when stream is ready. */ stream->transport = tp; @@ -2947,6 +2956,11 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, if (status != PJ_SUCCESS) goto err_cleanup; + /* Also add ref the transport group lock */ + if (stream->transport->grp_lock) + pj_grp_lock_add_ref(stream->transport->grp_lock); + + #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) /* Enable RTCP XR and update stream info/config to RTCP XR */ if (info->rtcp_xr_enabled) { @@ -3110,6 +3124,10 @@ static void stream_on_destroy(void *arg) /* This function may be called when stream is partly initialized. */ + /* Release ref to transport */ + if (stream->transport && stream->transport->grp_lock) + pj_grp_lock_dec_ref(stream->transport->grp_lock); + /* Free codec. */ if (stream->codec) { pjmedia_codec_close(stream->codec); diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c index a109bfb1e2..86c8db2e08 100644 --- a/pjmedia/src/pjmedia/vid_stream.c +++ b/pjmedia/src/pjmedia/vid_stream.c @@ -816,6 +816,9 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) return; } + /* Add ref counter to avoid premature destroy from callbacks */ + pj_grp_lock_add_ref(stream->grp_lock); + /* Ignore the packet if decoder is paused */ if (channel->paused) goto on_return; @@ -1042,6 +1045,8 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) stream->initial_rr = PJ_TRUE; } } + + pj_grp_lock_dec_ref(stream->grp_lock); } @@ -2058,6 +2063,10 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( stream->transport = tp; + /* Also add ref the transport group lock */ + if (stream->transport->grp_lock) + pj_grp_lock_add_ref(stream->transport->grp_lock); + /* Send RTCP SDES */ if (!stream->rtcp_sdes_bye_disabled) { pjmedia_vid_stream_send_rtcp_sdes(stream); @@ -2233,6 +2242,10 @@ static void on_destroy( void *arg ) PJ_LOG(4,(THIS_FILE, "Destroying %s..", stream->name.ptr)); + /* Release ref to transport */ + if (stream->transport && stream->transport->grp_lock) + pj_grp_lock_dec_ref(stream->transport->grp_lock); + /* Free codec. */ if (stream->codec) { pjmedia_vid_codec_close(stream->codec); From d2b0de1524f7fe97c0a1a62738275060bc4f76b8 Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 29 Nov 2024 18:19:16 +0800 Subject: [PATCH 123/491] Fixed SSL check in SIP auth client (#4187) --- pjsip/src/pjsip/sip_auth_client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c index bfccc931eb..4c5d7b4606 100644 --- a/pjsip/src/pjsip/sip_auth_client.c +++ b/pjsip/src/pjsip/sip_auth_client.c @@ -33,7 +33,8 @@ #include -#if defined(PJ_HAS_SSL_SOCK) && PJ_SSL_SOCK_IMP==PJ_SSL_SOCK_IMP_OPENSSL +#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \ + PJ_SSL_SOCK_IMP==PJ_SSL_SOCK_IMP_OPENSSL # include # include # include From f9c387d34a5422702f961be5ea242ead925d67bd Mon Sep 17 00:00:00 2001 From: ablangy <32962735+ablangy@users.noreply.github.com> Date: Mon, 2 Dec 2024 02:01:41 +0100 Subject: [PATCH 124/491] Issue 4180 heap use after free cloned hdr (#4182) --- pjsip/src/pjsip/sip_msg.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/pjsip/src/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c index 6f8ddb2abe..06c8cce860 100644 --- a/pjsip/src/pjsip/sip_msg.c +++ b/pjsip/src/pjsip/sip_msg.c @@ -26,6 +26,7 @@ #include #include #include +#include #include PJ_DEF_DATA(const pjsip_method) pjsip_invite_method = @@ -898,8 +899,11 @@ static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr, static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool, const pjsip_generic_int_hdr *rhs) { - pjsip_generic_int_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_generic_int_hdr); - pj_memcpy(hdr, rhs, sizeof(*hdr)); + pjsip_generic_int_hdr *hdr; + + hdr = pjsip_generic_int_hdr_create(pool, &rhs->name, rhs->ivalue); + hdr->type = rhs->type; + return hdr; } @@ -979,10 +983,12 @@ static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool, const pjsip_generic_array_hdr *rhs) { unsigned i; - pjsip_generic_array_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_generic_array_hdr); + pjsip_generic_array_hdr *hdr; - pj_memcpy(hdr, rhs, sizeof(*hdr)); - for (i=0; icount; ++i) { + hdr = pjsip_generic_array_hdr_create(pool, &rhs->name); + hdr->type = rhs->type; + hdr->count = PJ_MIN(rhs->count, PJSIP_GENERIC_ARRAY_MAX_COUNT); + for (i=0; icount; ++i) { pj_strdup(pool, &hdr->values[i], &rhs->values[i]); } From f56fd48e23982c47f38574a3fd93ebf248ef3762 Mon Sep 17 00:00:00 2001 From: Guoguo <16666742+imguoguo@users.noreply.github.com> Date: Mon, 2 Dec 2024 09:02:00 +0800 Subject: [PATCH 125/491] Fix build failure on RISC-V architecture (#4173) --- aconfigure | 6 ++++++ aconfigure.ac | 6 ++++++ config.guess | 3 +++ config.sub | 2 ++ third_party/webrtc/PJSIP_NOTES | 24 +++++++++++++++++++++++- third_party/webrtc/src/webrtc/typedefs.h | 7 +++++++ 6 files changed, 47 insertions(+), 1 deletion(-) diff --git a/aconfigure b/aconfigure index d1b3dba45a..b5d3446319 100755 --- a/aconfigure +++ b/aconfigure @@ -11035,6 +11035,9 @@ printf "%s\n" "Checking if libwebrtc is disabled...no" >&6; } ac_webrtc_instset=neon ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" ;; + riscv*) + ac_webrtc_instset=generic + ;; *) ac_webrtc_instset=sse2 ;; @@ -11175,6 +11178,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_webrtc_aec3_instset=neon ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" ;; + riscv*) + ac_webrtc_aec3_instset=generic + ;; *) ac_webrtc_aec3_instset=sse2 ;; diff --git a/aconfigure.ac b/aconfigure.ac index d49cb18ea8..279870e9d6 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -2743,6 +2743,9 @@ AC_ARG_ENABLE(libwebrtc, ac_webrtc_instset=neon ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" ;; + riscv*) + ac_webrtc_instset=generic + ;; *) ac_webrtc_instset=sse2 ;; @@ -2850,6 +2853,9 @@ AC_ARG_ENABLE(libwebrtc_aec3, ac_webrtc_aec3_instset=neon ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" ;; + riscv*) + ac_webrtc_aec3_instset=generic + ;; *) ac_webrtc_aec3_instset=sse2 ;; diff --git a/config.guess b/config.guess index aa04f04bda..b6f948fdc5 100755 --- a/config.guess +++ b/config.guess @@ -979,6 +979,9 @@ EOF ppc:Linux:*:*) echo powerpc-unknown-linux-gnu exit ;; + riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux exit ;; diff --git a/config.sub b/config.sub index 62220cd592..b4c73e19d8 100755 --- a/config.sub +++ b/config.sub @@ -304,6 +304,7 @@ case $basic_machine in | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ + | riscv | riscv32 | riscv32be | riscv64 | riscv64be \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ @@ -419,6 +420,7 @@ case $basic_machine in | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ + | riscv-* | riscv32-* | riscv32be-* | riscv64-* | riscv64be-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ diff --git a/third_party/webrtc/PJSIP_NOTES b/third_party/webrtc/PJSIP_NOTES index 4904e0b78a..31963d6f80 100644 --- a/third_party/webrtc/PJSIP_NOTES +++ b/third_party/webrtc/PJSIP_NOTES @@ -31,4 +31,26 @@ index 9385befc9..3460ec880 100644 + // Processor architecture detection. For more info on what's defined, see: // http://msdn.microsoft.com/en-us/library/b0084kay.aspx - // http://www.agner.org/optimize/calling_conventions.pdf \ No newline at end of file + // http://www.agner.org/optimize/calling_conventions.pdf + + +3. Add support for RISC-V + +diff --git a/third_party/webrtc/src/webrtc/typedefs.h b/third_party/webrtc/src/webrtc/typedefs.h +index 9385befc96..308f5fe982 100644 +--- a/third_party/webrtc/src/webrtc/typedefs.h ++++ b/third_party/webrtc/src/webrtc/typedefs.h +@@ -47,6 +47,13 @@ + #elif defined(__pnacl__) + #define WEBRTC_ARCH_32_BITS + #define WEBRTC_ARCH_LITTLE_ENDIAN ++#elif defined(__riscv) || defined(__riscv__) ++#define WEBRTC_ARCH_LITTLE_ENDIAN ++#if __riscv_xlen == 64 ++#define WEBRTC_ARCH_64_BITS ++#else ++#define WEBRTC_ARCH_32_BITS ++#endif + #else + #error Please add support for your architecture in typedefs.h + #endif \ No newline at end of file diff --git a/third_party/webrtc/src/webrtc/typedefs.h b/third_party/webrtc/src/webrtc/typedefs.h index 3460ec8807..9a565df8b7 100644 --- a/third_party/webrtc/src/webrtc/typedefs.h +++ b/third_party/webrtc/src/webrtc/typedefs.h @@ -49,6 +49,13 @@ #elif defined(__pnacl__) #define WEBRTC_ARCH_32_BITS #define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(__riscv) || defined(__riscv__) +#define WEBRTC_ARCH_LITTLE_ENDIAN +#if __riscv_xlen == 64 +#define WEBRTC_ARCH_64_BITS +#else +#define WEBRTC_ARCH_32_BITS +#endif #else #error Please add support for your architecture in typedefs.h #endif From 0520fbc92cb48739da9ec037024ab2aae9281820 Mon Sep 17 00:00:00 2001 From: sauwming Date: Mon, 2 Dec 2024 10:15:27 +0800 Subject: [PATCH 126/491] Suppress build warning in Metal (#4188) --- pjmedia/src/pjmedia-videodev/metal_dev.m | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pjmedia/src/pjmedia-videodev/metal_dev.m b/pjmedia/src/pjmedia-videodev/metal_dev.m index f12bfa8d99..b908c8b71b 100644 --- a/pjmedia/src/pjmedia-videodev/metal_dev.m +++ b/pjmedia/src/pjmedia-videodev/metal_dev.m @@ -626,7 +626,6 @@ static pj_status_t metal_factory_create_stream( pjmedia_video_format_detail *vfd; const pjmedia_video_format_info *vfi; metal_fmt_info *mfi; - pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL); PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO && @@ -678,11 +677,6 @@ static pj_status_t metal_factory_create_stream( *p_vid_strm = &strm->base; return PJ_SUCCESS; - -on_error: - metal_stream_destroy((pjmedia_vid_dev_stream *)strm); - - return status; } /* API: Get stream info. */ From 21804cd64d9d9db9e1a07667e2ea752bbc6bbf77 Mon Sep 17 00:00:00 2001 From: sauwming Date: Mon, 2 Dec 2024 10:20:14 +0800 Subject: [PATCH 127/491] Fixed video media transport leak (#4189) --- pjmedia/src/pjmedia/vid_stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c index 86c8db2e08..f3a74b85ff 100644 --- a/pjmedia/src/pjmedia/vid_stream.c +++ b/pjmedia/src/pjmedia/vid_stream.c @@ -2216,7 +2216,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy( pjmedia_vid_stream *stream ) */ if (stream->transport) { pjmedia_transport_detach(stream->transport, stream); - stream->transport = NULL; + //stream->transport = NULL; } /* This function may be called when stream is partly initialized, From d9357117ecfa666d9925d5e4908d17c164dbf44f Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Mon, 2 Dec 2024 16:16:25 +0700 Subject: [PATCH 128/491] Miscellaneous fixes (compile warnings, pool '%p' naming in pool debug). (#4191) --- pjlib/src/pj/pool_dbg.c | 20 ++++++++++++++------ pjlib/src/pj/timer.c | 12 +++++++----- pjsip/src/pjsip/sip_auth_client.c | 5 +++-- pjsip/src/pjsua-lib/pjsua_core.c | 2 +- pjsip/src/pjsua2/media.cpp | 7 +++++++ 5 files changed, 32 insertions(+), 14 deletions(-) diff --git a/pjlib/src/pj/pool_dbg.c b/pjlib/src/pj/pool_dbg.c index 380ba02730..5bc22c04cc 100644 --- a/pjlib/src/pj/pool_dbg.c +++ b/pjlib/src/pj/pool_dbg.c @@ -72,7 +72,14 @@ PJ_DEF(pj_pool_t*) pj_pool_create_imp( const char *file, int line, return NULL; if (name) { - pj_ansi_strxcpy(pool->obj_name, name, sizeof(pool->obj_name)); + char *p = pj_ansi_strchr(name, '%'); + if (p && *(p+1)=='p' && *(p+2)=='\0') { + /* Special name with "%p" suffix */ + pj_ansi_snprintf(pool->obj_name, sizeof(pool->obj_name), + name, pool); + } else { + pj_ansi_strxcpy(pool->obj_name, name, PJ_MAX_OBJ_NAME); + } } else { pj_ansi_strxcpy(pool->obj_name, "altpool", sizeof(pool->obj_name)); } @@ -171,8 +178,9 @@ PJ_DEF(void*) pj_pool_alloc_imp( const char *file, int line, { char msg[120]; pj_ansi_snprintf(msg, sizeof(msg), - "Mem %X (%d+%d bytes) allocated by %s:%d\r\n", - mem, sz, sizeof(struct pj_pool_mem), + "Mem %X (%u+%u bytes) allocated by %s:%d\r\n", + (unsigned)(intptr_t)mem, (unsigned)sz, + (unsigned)sizeof(struct pj_pool_mem), file, line); TRACE_(msg); } @@ -182,8 +190,8 @@ PJ_DEF(void*) pj_pool_alloc_imp( const char *file, int line, } /* Allocate memory from the pool and zero the memory */ -PJ_DEF(void*) pj_pool_calloc_imp( const char *file, int line, - pj_pool_t *pool, unsigned cnt, +PJ_DEF(void*) pj_pool_calloc_imp( const char *file, int line, + pj_pool_t *pool, unsigned cnt, unsigned elemsz) { void *mem; @@ -200,7 +208,7 @@ PJ_DEF(void*) pj_pool_calloc_imp( const char *file, int line, PJ_DEF(void*) pj_pool_zalloc_imp( const char *file, int line, pj_pool_t *pool, pj_size_t sz) { - return pj_pool_calloc_imp(file, line, pool, 1, sz); + return pj_pool_calloc_imp(file, line, pool, 1, (unsigned)sz); } diff --git a/pjlib/src/pj/timer.c b/pjlib/src/pj/timer.c index bbbaf91d59..86766ddfb4 100644 --- a/pjlib/src/pj/timer.c +++ b/pjlib/src/pj/timer.c @@ -368,7 +368,7 @@ static pj_timer_entry_dup * remove_node( pj_timer_heap_t *ht, size_t slot) static pj_status_t grow_heap(pj_timer_heap_t *ht) { // All the containers will double in size from max_size_ - size_t new_size = ht->max_size * 2; + pj_size_t new_size = ht->max_size * 2; #if PJ_TIMER_USE_COPY pj_timer_entry_dup *new_timer_dups = 0; #endif @@ -386,15 +386,16 @@ static pj_status_t grow_heap(pj_timer_heap_t *ht) (unsigned long)new_size)); // First grow the heap itself. - new_heap = (pj_timer_entry_dup**) - pj_pool_calloc(ht->pool, new_size, sizeof(pj_timer_entry_dup*)); + new_heap = (pj_timer_entry_dup**) + pj_pool_calloc(ht->pool, (unsigned)new_size, + sizeof(pj_timer_entry_dup*)); if (!new_heap) return PJ_ENOMEM; #if PJ_TIMER_USE_COPY // Grow the array of timer copies. - new_timer_dups = (pj_timer_entry_dup*) + new_timer_dups = (pj_timer_entry_dup*) pj_pool_alloc(ht->pool, sizeof(pj_timer_entry_dup) * new_size); if (!new_timer_dups) @@ -616,7 +617,8 @@ PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool, // Create the heap array. ht->heap = (pj_timer_entry_dup**) - pj_pool_calloc(pool, size, sizeof(pj_timer_entry_dup*)); + pj_pool_calloc(pool, (unsigned)size, + sizeof(pj_timer_entry_dup*)); if (!ht->heap) return PJ_ENOMEM; diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c index 4c5d7b4606..01563e4b75 100644 --- a/pjsip/src/pjsip/sip_auth_client.c +++ b/pjsip/src/pjsip/sip_auth_client.c @@ -246,7 +246,7 @@ PJ_DEF(pj_status_t) pjsip_auth_create_digest2( pj_str_t *result, digest_strlen = algorithm->digest_str_length; dig_len = digest_len; - if (result->slen < digest_strlen) { + if (result->slen < (pj_ssize_t)digest_strlen) { PJ_LOG(4, (THIS_FILE, "The length of the result buffer must be at least %d bytes " "for algorithm %.*s", digest_strlen, @@ -273,7 +273,8 @@ PJ_DEF(pj_status_t) pjsip_auth_create_digest2( pj_str_t *result, pjsip_auth_algorithms[algorithm_type].iana_name.ptr)); return PJ_EINVAL; } - PJ_ASSERT_RETURN(cred_info->data.slen >= digest_strlen, PJ_EINVAL); + PJ_ASSERT_RETURN(cred_info->data.slen >= (pj_ssize_t)digest_strlen, + PJ_EINVAL); } md = EVP_get_digestbyname(algorithm->openssl_name); diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index cc6976adbc..a29b5cdd6c 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -3376,7 +3376,7 @@ PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url) pool = pj_pool_create(&pjsua_var.cp.factory, "check%p", 1024, 0, NULL); if (!pool) return PJ_ENOMEM; - url = (char*) pj_pool_calloc(pool, 1, len+1); + url = (char*) pj_pool_calloc(pool, 1, (unsigned)len+1); pj_ansi_strxcpy(url, c_url, len+1); p = pjsip_parse_uri(pool, url, len, 0); diff --git a/pjsip/src/pjsua2/media.cpp b/pjsip/src/pjsua2/media.cpp index 3b6667644c..cefa7b3716 100644 --- a/pjsip/src/pjsua2/media.cpp +++ b/pjsip/src/pjsua2/media.cpp @@ -1591,6 +1591,13 @@ void MediaFormatVideo::init(pj_uint32_t formatId, this->avgBps = avgBps_; this->maxBps = maxBps_; #else + PJ_UNUSED_ARG(formatId); + PJ_UNUSED_ARG(width_); + PJ_UNUSED_ARG(height_); + PJ_UNUSED_ARG(fpsNum_); + PJ_UNUSED_ARG(fpsDenum_); + PJ_UNUSED_ARG(avgBps_); + PJ_UNUSED_ARG(maxBps_); type = PJMEDIA_TYPE_UNKNOWN; #endif } From fbdc7f2b4057ef45a20dd1b62ca94bb2c114329d Mon Sep 17 00:00:00 2001 From: sauwming Date: Tue, 3 Dec 2024 10:34:13 +0800 Subject: [PATCH 129/491] Fixed SIP transport selection used to reach destination (#4192) --- pjsip/src/pjsip/sip_transaction.c | 6 ++++++ pjsip/src/pjsip/sip_util.c | 24 +++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c index 8a6c18e1b8..4335f11ff5 100644 --- a/pjsip/src/pjsip/sip_transaction.c +++ b/pjsip/src/pjsip/sip_transaction.c @@ -2520,6 +2520,12 @@ static pj_status_t tsx_send_msg( pjsip_transaction *tsx, if (status == PJ_EPENDING) status = PJ_SUCCESS; if (status != PJ_SUCCESS) { + char errmsg[PJ_ERR_MSG_SIZE]; + pj_str_t err; + + err = pj_strerror(status, errmsg, sizeof(errmsg)); + tsx_set_status_code(tsx, PJSIP_SC_TSX_TRANSPORT_ERROR, &err); + tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT); pj_grp_lock_dec_ref(tsx->grp_lock); pjsip_tx_data_dec_ref(tdata); diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c index 1167093166..886228dbc5 100644 --- a/pjsip/src/pjsip/sip_util.c +++ b/pjsip/src/pjsip/sip_util.c @@ -1026,11 +1026,29 @@ PJ_DEF(pj_status_t) pjsip_process_route_set(pjsip_tx_data *tdata, return status; /* If transport selector is set, set destination type accordingly */ - if (tdata->tp_sel.type != PJSIP_TPSELECTOR_NONE && tdata->tp_sel.u.ptr) { + if ((tdata->tp_sel.type == PJSIP_TPSELECTOR_TRANSPORT || + tdata->tp_sel.type == PJSIP_TPSELECTOR_LISTENER) && + tdata->tp_sel.u.ptr) + { + pjsip_transport_type_e tp_type; + if (tdata->tp_sel.type == PJSIP_TPSELECTOR_TRANSPORT) - dest_info->type = tdata->tp_sel.u.transport->key.type; + tp_type = tdata->tp_sel.u.transport->key.type; else if (tdata->tp_sel.type == PJSIP_TPSELECTOR_LISTENER) - dest_info->type = tdata->tp_sel.u.listener->type; + tp_type = tdata->tp_sel.u.listener->type; + + /* Check if the transport selector type is compatible with + * the destination type. + */ + if (dest_info->type != PJSIP_TRANSPORT_UNSPECIFIED && + ((dest_info->type | PJSIP_TRANSPORT_IPV6) != + (tp_type | PJSIP_TRANSPORT_IPV6))) + { + PJ_LOG(4,(THIS_FILE, "Unsuitable transport selected to " + "reach destination")); + return PJSIP_ETPNOTSUITABLE; + } + dest_info->type = tp_type; } /* If target URI is different than request URI, replace From 2ecf7601ecbc0a9194941fc4dde49f5288f27255 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 3 Dec 2024 13:20:53 +0700 Subject: [PATCH 130/491] Changed version to 2.15 --- pjlib/include/pj/config.h | 4 ++-- version.mak | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h index 6736d47344..363520cb82 100644 --- a/pjlib/include/pj/config.h +++ b/pjlib/include/pj/config.h @@ -1496,7 +1496,7 @@ PJ_BEGIN_DECL #define PJ_VERSION_NUM_MAJOR 2 /** PJLIB version minor number. */ -#define PJ_VERSION_NUM_MINOR 14 +#define PJ_VERSION_NUM_MINOR 15 /** PJLIB version revision number. */ #define PJ_VERSION_NUM_REV 0 @@ -1505,7 +1505,7 @@ PJ_BEGIN_DECL * Extra suffix for the version (e.g. "-trunk"), or empty for * web release version. */ -#define PJ_VERSION_NUM_EXTRA "-dev" +#define PJ_VERSION_NUM_EXTRA "" /** * PJLIB version number consists of three bytes with the following format: diff --git a/version.mak b/version.mak index 5fa4f4d9ca..82782d3010 100644 --- a/version.mak +++ b/version.mak @@ -1,8 +1,8 @@ # Don't change the "export PJ_VERSION_xxx" style, they are parsed by setup.py export PJ_VERSION_MAJOR := 2 -export PJ_VERSION_MINOR := 14 +export PJ_VERSION_MINOR := 15 export PJ_VERSION_REV := -export PJ_VERSION_SUFFIX := -dev +export PJ_VERSION_SUFFIX := export PJ_VERSION := $(PJ_VERSION_MAJOR).$(PJ_VERSION_MINOR) From 22991e4cd716da132bbcbde776eb5a47bf3bcdc6 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 3 Dec 2024 14:57:34 +0700 Subject: [PATCH 131/491] Changed version to 2.15-dev --- pjlib/include/pj/config.h | 2 +- version.mak | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h index 363520cb82..49ff05ba32 100644 --- a/pjlib/include/pj/config.h +++ b/pjlib/include/pj/config.h @@ -1505,7 +1505,7 @@ PJ_BEGIN_DECL * Extra suffix for the version (e.g. "-trunk"), or empty for * web release version. */ -#define PJ_VERSION_NUM_EXTRA "" +#define PJ_VERSION_NUM_EXTRA "-dev" /** * PJLIB version number consists of three bytes with the following format: diff --git a/version.mak b/version.mak index 82782d3010..8d272887bb 100644 --- a/version.mak +++ b/version.mak @@ -2,7 +2,7 @@ export PJ_VERSION_MAJOR := 2 export PJ_VERSION_MINOR := 15 export PJ_VERSION_REV := -export PJ_VERSION_SUFFIX := +export PJ_VERSION_SUFFIX := -dev export PJ_VERSION := $(PJ_VERSION_MAJOR).$(PJ_VERSION_MINOR) From 8117bc593929b50f5f55402eae68b0bd7e4350ef Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 4 Dec 2024 12:16:34 +0800 Subject: [PATCH 132/491] Fixed warnings of duplicate libs (#4177) --- aconfigure | 5 ++--- aconfigure.ac | 5 ++--- build.mak.in | 1 - 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/aconfigure b/aconfigure index b5d3446319..8714b57bd6 100755 --- a/aconfigure +++ b/aconfigure @@ -7064,7 +7064,7 @@ if ac_fn_c_try_link "$LINENO" then : CFLAGS="$CFLAGS -DPJNATH_HAS_UPNP=1 $UPNP_CFLAGS" - LDFLAGS="$LDFLAGS $UPNP_LDFLAGS $UPNP_LIBS" + LDFLAGS="$LDFLAGS $UPNP_LDFLAGS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } @@ -8566,7 +8566,6 @@ printf "%s\n" "not found" >&6; } ac_sdl_cflags="-DPJMEDIA_VIDEO_DEV_HAS_SDL=1 $ac_sdl_cflags" ac_sdl_ldflags=`$SDL_CONFIG --libs` ac_sdl_ldflags=`echo "${ac_sdl_ldflags}" | sed -e 's/-mwindows//g'` - LIBS="$LIBS $ac_sdl_ldflags" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Unsupported SDL version" >&5 printf "%s\n" "Unsupported SDL version" >&6; } @@ -9231,7 +9230,7 @@ if ac_fn_c_try_link "$LINENO" then : ac_vpx_cflags="-DPJMEDIA_HAS_VPX_CODEC=1 $VPX_CFLAGS" - ac_vpx_ldflags="$VPX_LDFLAGS $VPX_LIBS" + ac_vpx_ldflags="$VPX_LDFLAGS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } diff --git a/aconfigure.ac b/aconfigure.ac index 279870e9d6..e70dec807f 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -750,7 +750,7 @@ AC_ARG_ENABLE(upnp, ], [ CFLAGS="$CFLAGS -DPJNATH_HAS_UPNP=1 $UPNP_CFLAGS" - LDFLAGS="$LDFLAGS $UPNP_LDFLAGS $UPNP_LIBS" + LDFLAGS="$LDFLAGS $UPNP_LDFLAGS" AC_MSG_RESULT(yes) ], [ @@ -1546,7 +1546,6 @@ AC_ARG_ENABLE(sdl, ac_sdl_cflags="-DPJMEDIA_VIDEO_DEV_HAS_SDL=1 $ac_sdl_cflags" ac_sdl_ldflags=`$SDL_CONFIG --libs` ac_sdl_ldflags=`echo "${ac_sdl_ldflags}" | sed -e 's/-mwindows//g'` - LIBS="$LIBS $ac_sdl_ldflags" else AC_MSG_RESULT([Unsupported SDL version]) fi @@ -1806,7 +1805,7 @@ AC_ARG_ENABLE(vpx, ], [ ac_vpx_cflags="-DPJMEDIA_HAS_VPX_CODEC=1 $VPX_CFLAGS" - ac_vpx_ldflags="$VPX_LDFLAGS $VPX_LIBS" + ac_vpx_ldflags="$VPX_LDFLAGS" AC_MSG_RESULT(yes) ], [ diff --git a/build.mak.in b/build.mak.in index 3955a8930c..336cc6a272 100644 --- a/build.mak.in +++ b/build.mak.in @@ -331,7 +331,6 @@ export APP_LDLIBS := $(PJSUA_LIB_LDLIB) \ $(PJLIB_LDLIB) \ @LIBS@ export APP_LDXXLIBS := $(PJSUA2_LIB_LDLIB) \ - -lstdc++ \ $(APP_LDLIBS) # Here are the variables to use if application is using the library From 4df94676f152043c8c93f3e4c263ba5bf2703477 Mon Sep 17 00:00:00 2001 From: George Joseph Date: Thu, 5 Dec 2024 20:19:25 -0700 Subject: [PATCH 133/491] Set default pjsip_cred_info.auth_algorithm to PJSIP_AUTH_ALGORITHM_MD5 (#4195) The new algorithm_type field in the pjsip_cred_info structure wasn't being defaulted to PJSIP_AUTH_ALGORITHM_MD5 as it should be. As a result if the user specified a data_type of PJSIP_CRED_DATA_DIGEST but didn't explicitly set algorithm_type, it was left at PJSIP_AUTH_ALGORITHM_NOT_SET which will caused authentication to fail. * pjsip_cred_info.auth_algorithm is now correctly defaulted to PJSIP_AUTH_ALGORITHM_MD5 when supplied via the pjsip_auth_lookup_cred and pjsip_auth_lookup_cred2 callbacks and via the pjsip_auth_clt_set_credentials, and pjsip_auth_create_digest functions. * pjsip_cred_info.auth_algorithm now defaults to PJSIP_AUTH_ALGORITHM_SHA256 for the pjsip_auth_create_digestSHA256 function. * Documentation was updated for those functions and callbacks to indicate the defaults. NOTE: The pjsip_auth_create_digest and pjsip_auth_create_digestSHA256 defaults are actually set in pjsip_auth_create_digest2 because those two functions are now deprecated wrappers that pass cred_info (which is a const) to pjsip_auth_create_digest2. The documentation for pjsip_auth_create_digest2 however, doesn't mention defaults on purpose because it's a generic function that handles multiple algorithms and we want users to specifify exactly what algorithms they want to use to avoid ambiguity. Resolves: #4194 --- pjsip/include/pjsip/sip_auth.h | 34 ++++++++++++++++++++++++++++--- pjsip/src/pjsip/sip_auth_client.c | 26 +++++++++++++++-------- pjsip/src/pjsip/sip_auth_server.c | 9 ++++++++ 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h index 95c8d136c1..5e11664d5c 100644 --- a/pjsip/include/pjsip/sip_auth.h +++ b/pjsip/include/pjsip/sip_auth.h @@ -373,6 +373,10 @@ PJ_DECL(int) pjsip_cred_info_cmp(const pjsip_cred_info *cred1, /** * Type of function to lookup credential for the specified name. * + * \note If pjsip_cred_info::data_type is set to PJSIP_CRED_DATA_DIGEST and + * pjsip_cred_info::algorithm_type is left unset (0), algorithm_type will + * default to #PJSIP_AUTH_ALGORITHM_MD5. + * * @param pool Pool to initialize the credential info. * @param realm Realm to find the account. * @param acc_name Account name to look for. @@ -406,6 +410,10 @@ typedef struct pjsip_auth_lookup_cred_param /** * Type of function to lookup credential for the specified name. * + * \note If pjsip_cred_info::data_type is set to PJSIP_CRED_DATA_DIGEST and + * pjsip_cred_info::algorithm_type is left unset (0), algorithm_type will + * default to #PJSIP_AUTH_ALGORITHM_MD5. + * * @param pool Pool to initialize the credential info. * @param param The input param for credential lookup. * @param cred_info The structure to put the credential when it's found. @@ -482,6 +490,10 @@ PJ_DECL(pj_status_t) pjsip_auth_clt_clone( pj_pool_t *pool, * Set the credentials to be used during the session. This will duplicate * the specified credentials using client authentication's pool. * + * \note If pjsip_cred_info::data_type is set to PJSIP_CRED_DATA_DIGEST and + * pjsip_cred_info::algorithm_type is left unset (0), algorithm_type will + * default to #PJSIP_AUTH_ALGORITHM_MD5. + * * @param sess The client authentication session. * @param cred_cnt Number of credentials. * @param c Array of credentials. @@ -711,6 +723,9 @@ PJ_DECL(pj_status_t) pjsip_auth_srv_challenge2(pjsip_auth_srv *auth_srv, * Helper function to create a digest out of the specified * parameters. * + * \deprecated Use #pjsip_auth_create_digest2 with + * algorithm_type = #PJSIP_AUTH_ALGORITHM_MD5. + * * \warning Because of ambiguities in the API, this function * should only be used for backward compatibility with the * MD5 digest algorithm. New code should use @@ -719,6 +734,10 @@ PJ_DECL(pj_status_t) pjsip_auth_srv_challenge2(pjsip_auth_srv *auth_srv, * pjsip_cred_info::data_type must be #PJSIP_CRED_DATA_PLAIN_PASSWD * or #PJSIP_CRED_DATA_DIGEST. * + * \note If pjsip_cred_info::data_type is set to PJSIP_CRED_DATA_DIGEST and + * pjsip_cred_info::algorithm_type is left unset (0), algorithm_type will + * default to #PJSIP_AUTH_ALGORITHM_MD5. + * * @param result String to store the response digest. This string * must have been preallocated by caller with the * buffer at least PJSIP_MD5STRLEN (32 bytes) in size. @@ -746,9 +765,21 @@ PJ_DECL(pj_status_t) pjsip_auth_create_digest(pj_str_t *result, /** * Helper function to create SHA-256 digest out of the specified * parameters. + * * \deprecated Use #pjsip_auth_create_digest2 with * algorithm_type = #PJSIP_AUTH_ALGORITHM_SHA256. * + * \warning Because of ambiguities in the API, this function + * should only be used for backward compatibility with the + * SHA256 digest algorithm. New code should use + * #pjsip_auth_create_digest2 + * + * pjsip_cred_info::data_type must be #PJSIP_CRED_DATA_PLAIN_PASSWD + * or #PJSIP_CRED_DATA_DIGEST. + * + * \note If pjsip_cred_info::data_type is set to PJSIP_CRED_DATA_DIGEST and + * pjsip_cred_info::algorithm_type is left unset (0), algorithm_type will + * default to #PJSIP_AUTH_ALGORITHM_SHA256. * * @param result String to store the response digest. This string * must have been preallocated by caller with the @@ -794,9 +825,6 @@ PJ_DECL(pj_status_t) pjsip_auth_create_digestSHA256(pj_str_t* result, * pjsip_cred_info::algorithm_type MUST match the algorithm_type * passed as the last parameter to this function. * - * \note If left unset (0), pjsip_cred_info::algorithm_type will - * default to #PJSIP_AUTH_ALGORITHM_MD5. - * * @param result String to store the response digest. This string * must have been preallocated by the caller with the * buffer at least as large as the digest_str_length diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c index 01563e4b75..989e7eaaf6 100644 --- a/pjsip/src/pjsip/sip_auth_client.c +++ b/pjsip/src/pjsip/sip_auth_client.c @@ -221,10 +221,7 @@ PJ_DEF(pj_status_t) pjsip_auth_create_digest2( pj_str_t *result, PJ_ASSERT_RETURN(result && nonce && uri && realm && cred_info && method, PJ_EINVAL); pj_bzero(result->ptr, result->slen); - algorithm = pjsip_auth_get_algorithm_by_type(algorithm_type == PJSIP_AUTH_ALGORITHM_NOT_SET - ? PJSIP_AUTH_ALGORITHM_MD5 - : algorithm_type); - + algorithm = pjsip_auth_get_algorithm_by_type(algorithm_type); if (!algorithm) { PJ_LOG(4, (THIS_FILE, "The algorithm_type is invalid")); return PJ_ENOTSUP; @@ -263,12 +260,16 @@ PJ_DEF(pj_status_t) pjsip_auth_create_digest2( pj_str_t *result, } if (PJSIP_CRED_DATA_IS_DIGEST(cred_info)) { - if (cred_info->algorithm_type != algorithm_type) { + pjsip_auth_algorithm_type cred_algorithm_type = cred_info->algorithm_type; + + if (cred_algorithm_type == PJSIP_AUTH_ALGORITHM_NOT_SET) { + cred_algorithm_type = algorithm_type; + } else if (cred_algorithm_type != algorithm_type) { PJ_LOG(4,(THIS_FILE, "The algorithm specified in the cred_info (%.*s) " "doesn't match the algorithm requested for hashing (%.*s)", - (int)pjsip_auth_algorithms[cred_info->algorithm_type].iana_name.slen, - pjsip_auth_algorithms[cred_info->algorithm_type].iana_name.ptr, + (int)pjsip_auth_algorithms[cred_algorithm_type].iana_name.slen, + pjsip_auth_algorithms[cred_algorithm_type].iana_name.ptr, (int)pjsip_auth_algorithms[algorithm_type].iana_name.slen, pjsip_auth_algorithms[algorithm_type].iana_name.ptr)); return PJ_EINVAL; @@ -917,7 +918,16 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess, pj_strdup(sess->pool, &sess->cred_info[i].realm, &c[i].realm); pj_strdup(sess->pool, &sess->cred_info[i].username, &c[i].username); pj_strdup(sess->pool, &sess->cred_info[i].data, &c[i].data); - sess->cred_info[i].algorithm_type = c[i].algorithm_type; + /* + * If the data type is DIGEST and an auth algorithm isn't set, + * default it to MD5. + */ + if (PJSIP_CRED_DATA_IS_DIGEST(&c[i]) && + c[i].algorithm_type == PJSIP_AUTH_ALGORITHM_NOT_SET) { + sess->cred_info[i].algorithm_type = PJSIP_AUTH_ALGORITHM_MD5; + } else { + sess->cred_info[i].algorithm_type = c[i].algorithm_type; + } } sess->cred_cnt = cred_cnt; } diff --git a/pjsip/src/pjsip/sip_auth_server.c b/pjsip/src/pjsip/sip_auth_server.c index edc3aabb41..746cba3465 100644 --- a/pjsip/src/pjsip/sip_auth_server.c +++ b/pjsip/src/pjsip/sip_auth_server.c @@ -248,6 +248,15 @@ PJ_DEF(pj_status_t) pjsip_auth_srv_verify( pjsip_auth_srv *auth_srv, } } + /* + * If the data type is DIGEST and an auth algorithm isn't set, + * default it to MD5. + */ + if (PJSIP_CRED_DATA_IS_DIGEST(&cred_info) && + cred_info.algorithm_type == PJSIP_AUTH_ALGORITHM_NOT_SET) { + cred_info.algorithm_type = PJSIP_AUTH_ALGORITHM_MD5; + } + /* Authenticate with the specified credential. */ status = pjsip_auth_verify(h_auth, &msg->line.req.method.name, &cred_info); From 903027422bd51f9e85d47219043c2bd93c6d1e17 Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 6 Dec 2024 16:35:45 +0800 Subject: [PATCH 134/491] Fixed bug in audio conference disconnection (#4198) --- pjmedia/src/pjmedia/conference.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c index 924100bda8..80bfa66c84 100644 --- a/pjmedia/src/pjmedia/conference.c +++ b/pjmedia/src/pjmedia/conference.c @@ -1380,7 +1380,7 @@ static void op_disconnect_ports(pjmedia_conf *conf, { unsigned src_slot, sink_slot; struct conf_port *src_port = NULL, *dst_port = NULL; - unsigned i; + int i; /* Ports must be valid. */ src_slot = prm->disconnect_ports.src; @@ -1440,13 +1440,16 @@ static void op_disconnect_ports(pjmedia_conf *conf, dst_port->name.ptr)); for (i=0; imax_ports; ++i) { - unsigned j; + int j; src_port = conf->ports[i]; if (!src_port || src_port->listener_cnt == 0) continue; - for (j=0; jlistener_cnt; ++j) { + /* We need to iterate backwards since the listener count + * can potentially decrease. + */ + for (j=src_port->listener_cnt-1; j>=0; --j) { if (src_port->listener_slots[j] == sink_slot) { op_param op_prm = {0}; op_prm.disconnect_ports.src = i; @@ -1465,7 +1468,10 @@ static void op_disconnect_ports(pjmedia_conf *conf, (int)src_port->name.slen, src_port->name.ptr)); - for (i=0; ilistener_cnt; ++i) { + /* We need to iterate backwards since the listener count + * will keep decreasing. + */ + for (i=src_port->listener_cnt-1; i>=0; --i) { op_param op_prm = {0}; op_prm.disconnect_ports.src = src_slot; op_prm.disconnect_ports.sink = src_port->listener_slots[i]; From fb4ccef56c65fca495e92af947032e362a4bbd52 Mon Sep 17 00:00:00 2001 From: LeonidGoltsblat <138720759+LeonidGoltsblat@users.noreply.github.com> Date: Tue, 10 Dec 2024 01:26:38 +0100 Subject: [PATCH 135/491] assertion when the ACK response is recognized as a merged request (#4203) --- pjsip/src/pjsua-lib/pjsua_core.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index a29b5cdd6c..457738ef75 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -667,15 +667,31 @@ static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata) pj_bool_t processed = PJ_FALSE; #if PJSUA_DETECT_MERGED_REQUESTS - if (pjsip_tsx_detect_merged_requests(rdata)) { - PJ_LOG(4, (THIS_FILE, "Merged request detected")); + pjsip_transaction *tsx; + if ((tsx = pjsip_tsx_detect_merged_requests(rdata)) != NULL) { - /* Respond with 482 (Loop Detected) */ - pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, - PJSIP_SC_LOOP_DETECTED, NULL, - NULL, NULL, NULL); + pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx); - return PJ_TRUE; + PJ_LOG(4, (THIS_FILE, "Merged request detected (%s) (%s): %s from %s:%d", + dlg ? dlg->obj_name : "-no-dlg-", + tsx->obj_name, + pjsip_rx_data_get_info(rdata), + rdata->pkt_info.src_name, + rdata->pkt_info.src_port)); + + /* Don't respond to ACK, even if it looks like a merged request + * Let it be "dropped/unhandled by any modules" + */ + if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, + &pjsip_ack_method) == 0) { + return PJ_FALSE; + } else { + /* Respond with 482 (Loop Detected) */ + pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, + PJSIP_SC_LOOP_DETECTED, NULL, + NULL, NULL, NULL); + return PJ_TRUE; + } } #endif From bb02bf3c4ed9f58f36e39d633d5a60de543637e4 Mon Sep 17 00:00:00 2001 From: sauwming Date: Tue, 10 Dec 2024 12:12:47 +0800 Subject: [PATCH 136/491] Fixed AudioMediaPort use after free (#4200) --- pjmedia/src/pjmedia/port.c | 19 ++++--- pjsip/include/pjsua2/media.hpp | 2 +- pjsip/src/pjsua2/media.cpp | 90 ++++++++++++++++++++++++++++------ 3 files changed, 89 insertions(+), 22 deletions(-) diff --git a/pjmedia/src/pjmedia/port.c b/pjmedia/src/pjmedia/port.c index cede0d0688..2286fd0439 100644 --- a/pjmedia/src/pjmedia/port.c +++ b/pjmedia/src/pjmedia/port.c @@ -159,16 +159,23 @@ PJ_DEF(pj_status_t) pjmedia_port_init_grp_lock( pjmedia_port *port, PJ_ASSERT_RETURN(port && pool, PJ_EINVAL); PJ_ASSERT_RETURN(port->grp_lock == NULL, PJ_EEXISTS); - /* We need to be caution on ports that do not have its own pool, - * such port is likely using app's pool, so if the app destroys the port + /* We need to be extra cautious here! + * If media port is using app's pool, and the app destroys the port * and then destroys the pool immediately, it may cause crash as the port * may have not really been destroyed and may still be accessed. - * When port has a pool, it usually implements on_destroy() for releasing - * the pool, so here we check availability of on_destroy implementation. + * + * Thus, media port needs to create and manage its own pool. Since + * app MUST NOT free this pool, port typically needs to implement + * on_destroy() for releasing the pool. Alternatively, port can also + * add a group lock handler for this purpose, but since we can't check + * this, here we just check the availability of on_destroy implementation. */ if (port->on_destroy == NULL) { - PJ_LOG(2,(THIS_FILE, "Warning, media port %s is using group lock, but " - "it does not seem to have a pool.", + PJ_LOG(2,(THIS_FILE, "Warning! Port %s on_destroy() not found. To " + "avoid premature destroy, media port must " + "manage its own pool, which can only be " + "released in on_destroy() or in its grp lock " + "handler. See PR #3928 for more info.", port->info.name.ptr)); } diff --git a/pjsip/include/pjsua2/media.hpp b/pjsip/include/pjsua2/media.hpp index 0d1631908f..fa84933c99 100644 --- a/pjsip/include/pjsua2/media.hpp +++ b/pjsip/include/pjsua2/media.hpp @@ -528,7 +528,7 @@ class AudioMediaPort : public AudioMedia private: pj_pool_t *pool; - pjmedia_port port; + pjmedia_port *port; }; /** diff --git a/pjsip/src/pjsua2/media.cpp b/pjsip/src/pjsua2/media.cpp index cefa7b3716..5e558a2374 100644 --- a/pjsip/src/pjsua2/media.cpp +++ b/pjsip/src/pjsua2/media.cpp @@ -281,26 +281,50 @@ AudioMedia* AudioMedia::typecastFromMedia(Media *media) /////////////////////////////////////////////////////////////////////////////// +struct port_data { + AudioMediaPort *mport; + pj_pool_t *pool; +}; + AudioMediaPort::AudioMediaPort() -: pool(NULL) +: pool(NULL), port(NULL) { - pj_bzero(&port, sizeof(port)); } AudioMediaPort::~AudioMediaPort() { - if (pool) { - PJSUA2_CATCH_IGNORE( unregisterMediaPort() ); - pj_pool_release(pool); - pool = NULL; + PJSUA2_CATCH_IGNORE( unregisterMediaPort() ); + if (port) { + struct port_data *pdata = static_cast + (port->port_data.pdata); + + /* Make sure port no longer accesses this object in its + * get/put_frame() callback. + */ + if (port->grp_lock) { + pj_grp_lock_acquire(port->grp_lock); + pdata->mport = NULL; + pj_grp_lock_release(port->grp_lock); + } + + pjmedia_port_destroy(port); + /* We release the pool later in port.on_destroy since + * the unregistration is async and may not have completed yet. + */ } } static pj_status_t get_frame(pjmedia_port *port, pjmedia_frame *frame) { - AudioMediaPort *mport = (AudioMediaPort *) port->port_data.pdata; + struct port_data *pdata = static_cast + (port->port_data.pdata); + AudioMediaPort *mport; MediaFrame frame_; + pj_grp_lock_acquire(port->grp_lock); + if ((mport = pdata->mport) == NULL) + goto on_return; + frame_.size = (unsigned)frame->size; mport->onFrameRequested(frame_); frame->type = frame_.type; @@ -315,20 +339,42 @@ static pj_status_t get_frame(pjmedia_port *port, pjmedia_frame *frame) pj_memcpy(frame->buf, frame_.buf.data(), frame->size); #endif - +on_return: + pj_grp_lock_release(port->grp_lock); return PJ_SUCCESS; } static pj_status_t put_frame(pjmedia_port *port, pjmedia_frame *frame) { - AudioMediaPort *mport = (AudioMediaPort *) port->port_data.pdata; + struct port_data *pdata = static_cast + (port->port_data.pdata); + AudioMediaPort *mport; MediaFrame frame_; + pj_grp_lock_acquire(port->grp_lock); + if ((mport = pdata->mport) == NULL) + goto on_return; + frame_.type = frame->type; frame_.buf.assign((char *)frame->buf, ((char *)frame->buf) + frame->size); frame_.size = (unsigned)frame->size; mport->onFrameReceived(frame_); +on_return: + pj_grp_lock_release(port->grp_lock); + return PJ_SUCCESS; +} + +static pj_status_t port_on_destroy(pjmedia_port *port) +{ + struct port_data *pdata = static_cast + (port->port_data.pdata); + pj_pool_t *pool = pdata->pool; + + if (pool) { + pj_pool_release(pool); + } + return PJ_SUCCESS; } @@ -337,6 +383,8 @@ void AudioMediaPort::createPort(const string &name, MediaFormatAudio &fmt) { pj_str_t name_; pjmedia_format fmt_; + struct port_data *pdata; + pj_status_t status; if (pool) { PJSUA2_RAISE_ERROR(PJ_EEXISTS); @@ -348,18 +396,30 @@ void AudioMediaPort::createPort(const string &name, MediaFormatAudio &fmt) } /* Init port. */ - pj_bzero(&port, sizeof(port)); + port = PJ_POOL_ZALLOC_T(pool, pjmedia_port); pj_strdup2_with_null(pool, &name_, name.c_str()); fmt_ = fmt.toPj(); - pjmedia_port_info_init2(&port.info, &name_, + pjmedia_port_info_init2(&port->info, &name_, PJMEDIA_SIG_CLASS_APP ('A', 'M', 'P'), PJMEDIA_DIR_ENCODING_DECODING, &fmt_); - port.port_data.pdata = this; - port.put_frame = &put_frame; - port.get_frame = &get_frame; + pdata = PJ_POOL_ZALLOC_T(pool, struct port_data); + pdata->mport = this; + pdata->pool = pool; + port->port_data.pdata = pdata; + port->put_frame = &put_frame; + port->get_frame = &get_frame; + + /* Must implement on_destroy() and create group lock to avoid + * race and premature destroy. + */ + port->on_destroy = &port_on_destroy; + status = pjmedia_port_init_grp_lock(port, pool, NULL); + if (status != PJ_SUCCESS) { + PJSUA2_RAISE_ERROR(status); + } - registerMediaPort2(&port, pool); + registerMediaPort2(port, pool); } /////////////////////////////////////////////////////////////////////////////// From 85b38f456c35d1cb9020c6b76aaffd33777bfbfa Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 10 Dec 2024 14:17:08 +0700 Subject: [PATCH 137/491] Add own pool to splitcomb's reverse channel. (#4208) --- pjmedia/src/pjmedia/splitcomb.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/pjmedia/src/pjmedia/splitcomb.c b/pjmedia/src/pjmedia/splitcomb.c index b66774a840..02bde6ef26 100644 --- a/pjmedia/src/pjmedia/splitcomb.c +++ b/pjmedia/src/pjmedia/splitcomb.c @@ -108,6 +108,7 @@ struct splitcomb struct reverse_port { pjmedia_port base; + pj_pool_t *pool; struct splitcomb*parent; unsigned ch_num; @@ -323,7 +324,7 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_set_channel( pjmedia_port *splitcomb, /* * Create reverse phase port for the specified channel. */ -PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool_, pjmedia_port *splitcomb, unsigned ch_num, unsigned options, @@ -336,11 +337,11 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool, unsigned buf_options; const pjmedia_audio_format_detail *sc_afd, *p_afd; pjmedia_port *port; + pj_pool_t* pool; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(splitcomb, PJ_EINVAL); - PJ_UNUSED_ARG(pool); /* Make sure this is really a splitcomb port */ PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL); @@ -348,13 +349,21 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool, /* Check the channel number */ PJ_ASSERT_RETURN(ch_num < PJMEDIA_PIA_CCNT(&sc->base.info), PJ_EINVAL); + /* Make sure that the channel has not been setup */ + PJ_ASSERT_RETURN(sc->port_desc[ch_num].port==NULL, PJ_EINVALIDOP); + /* options is unused for now */ PJ_UNUSED_ARG(options); sc_afd = pjmedia_format_get_audio_format_detail(&splitcomb->info.fmt, 1); + /* Create own pool */ + pool = pj_pool_create(pool_->factory, "sc-revch", 500, 500, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + /* Create the port */ - rport = PJ_POOL_ZALLOC_T(sc->pool, struct reverse_port); + rport = PJ_POOL_ZALLOC_T(pool, struct reverse_port); + rport->pool = pool; rport->parent = sc; rport->ch_num = ch_num; @@ -393,6 +402,7 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool, buf_options, &rport->buf[DIR_DOWNSTREAM].dbuf); if (status != PJ_SUCCESS) { + rport_on_destroy(port); return status; } @@ -405,7 +415,7 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool, buf_options, &rport->buf[DIR_UPSTREAM].dbuf); if (status != PJ_SUCCESS) { - pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf); + rport_on_destroy(port); return status; } @@ -419,9 +429,9 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool, sc->port_desc[ch_num].reversed = PJ_TRUE; /* Init group lock */ - status = pjmedia_port_init_grp_lock(port, sc->pool, sc->base.grp_lock); + status = pjmedia_port_init_grp_lock(port, pool, sc->base.grp_lock); if (status != PJ_SUCCESS) { - rport_on_destroy(&rport->base); + rport_on_destroy(port); return status; } @@ -846,8 +856,13 @@ static pj_status_t rport_on_destroy(pjmedia_port *this_port) { struct reverse_port *rport = (struct reverse_port*) this_port; - pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf); - pjmedia_delay_buf_destroy(rport->buf[DIR_UPSTREAM].dbuf); + if (rport->buf[DIR_DOWNSTREAM].dbuf) + pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf); + + if (rport->buf[DIR_UPSTREAM].dbuf) + pjmedia_delay_buf_destroy(rport->buf[DIR_UPSTREAM].dbuf); + + pj_pool_safe_release(&rport->pool); return PJ_SUCCESS; } From 484c6b6126d3922706938db4f2db33329f7f0cf3 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Thu, 12 Dec 2024 09:39:14 +0200 Subject: [PATCH 138/491] pcaputil: Support parsing decoder options from FMTP input (#4205) Some codecs need specific flags that are given in the SDP media description. For example, AMR has octet-align=1. Instead of adding distinct flags for each codec, allow pcaputil to accept the fmtp content and parse the attributes from there. Example: pcaputil \ --dst-port=52422 \ --codec=AMR/8000 \ --codec-fmtp='mode-set=0,1,2,3,4,5,6,7;octet-align=1' \ amr.pcap amr.wav --- pjmedia/include/pjmedia/stream_common.h | 15 +++++++++++++++ pjmedia/src/pjmedia/stream_common.c | 13 ++++++++++--- pjsip-apps/src/samples/pcaputil.c | 11 +++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/pjmedia/include/pjmedia/stream_common.h b/pjmedia/include/pjmedia/stream_common.h index 61a0c1708e..bcbd7a59be 100644 --- a/pjmedia/include/pjmedia/stream_common.h +++ b/pjmedia/include/pjmedia/stream_common.h @@ -117,6 +117,21 @@ PJ_DECL(pj_status_t) pjmedia_stream_info_parse_fmtp(pj_pool_t *pool, pjmedia_codec_fmtp *fmtp); +/** + * This is an internal function for parsing fmtp data from a raw buffer. + * + * @param pool Pool to allocate memory, if pool is NULL, the fmtp + * string pointers will point to the original string. + * @param str The fmtp string to be parsed. + * @param fmtp The format parameter to store the parsing result. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_stream_info_parse_fmtp_data(pj_pool_t *pool, + const pj_str_t *str, + pjmedia_codec_fmtp *fmtp); + + PJ_END_DECL diff --git a/pjmedia/src/pjmedia/stream_common.c b/pjmedia/src/pjmedia/stream_common.c index 75b5ac3957..95bb1a64fe 100644 --- a/pjmedia/src/pjmedia/stream_common.c +++ b/pjmedia/src/pjmedia/stream_common.c @@ -43,7 +43,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_parse_fmtp( pj_pool_t *pool, { const pjmedia_sdp_attr *attr; pjmedia_sdp_fmtp sdp_fmtp; - char *p, *p_end, fmt_buf[8]; + char fmt_buf[8]; pj_str_t fmt; pj_status_t status; @@ -63,9 +63,16 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_parse_fmtp( pj_pool_t *pool, if (status != PJ_SUCCESS) return status; + return pjmedia_stream_info_parse_fmtp_data(pool, &sdp_fmtp.fmt_param, fmtp); +} + +PJ_DECL(pj_status_t) pjmedia_stream_info_parse_fmtp_data(pj_pool_t *pool, + const pj_str_t *str, + pjmedia_codec_fmtp *fmtp) +{ /* Prepare parsing */ - p = sdp_fmtp.fmt_param.ptr; - p_end = p + sdp_fmtp.fmt_param.slen; + char *p = str->ptr; + char *p_end = p + str->slen; /* Parse */ while (p < p_end) { diff --git a/pjsip-apps/src/samples/pcaputil.c b/pjsip-apps/src/samples/pcaputil.c index 0cb746c2dc..f87bd43dc3 100644 --- a/pjsip-apps/src/samples/pcaputil.c +++ b/pjsip-apps/src/samples/pcaputil.c @@ -54,6 +54,8 @@ static const char *USAGE = " AES_CM_128_HMAC_SHA1_80 \n" " AES_CM_128_HMAC_SHA1_32\n" " --srtp-key=KEY, -k Set the base64 key to decrypt SRTP packets.\n" +" --codec-fmtp=FMTP Set the fmtp input for parsing codec options.\n" +" For example: \"mode-set=0;octet-align=1\".\n" #if PJMEDIA_HAS_OPUS_CODEC " --opus-ch=CH Opus channel count \n" " --opus-clock-rate=CR Opus clock rate \n" @@ -93,6 +95,7 @@ struct args pjmedia_aud_dev_index dev_id; pj_str_t srtp_crypto; pj_str_t srtp_key; + pj_str_t codec_fmtp; #if PJMEDIA_HAS_OPUS_CODEC int opus_clock_rate; int opus_ch; @@ -338,6 +341,9 @@ static void pcap2wav(const struct args *args) ci = info[0]; } T( pjmedia_codec_mgr_get_default_param(cmgr, ci, ¶m) ); + if (args->codec_fmtp.slen > 0) { + T( pjmedia_stream_info_parse_fmtp_data(app.pool, &args->codec_fmtp, ¶m.setting.dec_fmtp) ); + } /* Alloc and init codec */ T( pjmedia_codec_mgr_alloc_codec(cmgr, ci, &app.codec) ); @@ -465,6 +471,7 @@ int main(int argc, char *argv[]) OPT_DST_PORT, OPT_CODEC, OPT_PLAY_DEV_ID, + OPT_CODEC_FMTP, #if PJMEDIA_HAS_OPUS_CODEC OPT_OPUS_CH = 'C', OPT_OPUS_CLOCK_RATE = 'K', @@ -481,6 +488,7 @@ int main(int argc, char *argv[]) { "dst-port", 1, 0, OPT_DST_PORT }, { "codec", 1, 0, OPT_CODEC }, { "play-dev-id", 1, 0, OPT_PLAY_DEV_ID }, + { "codec-fmtp", 1, 0, OPT_CODEC_FMTP }, #if PJMEDIA_HAS_OPUS_CODEC { "opus-ch", 1, 0, OPT_OPUS_CH }, { "opus-clock-rate", 1, 0, OPT_OPUS_CLOCK_RATE }, @@ -548,6 +556,9 @@ int main(int argc, char *argv[]) case OPT_PLAY_DEV_ID: args.dev_id = atoi(pj_optarg); break; + case OPT_CODEC_FMTP: + args.codec_fmtp = pj_str(pj_optarg); + break; #if PJMEDIA_HAS_OPUS_CODEC case OPT_OPUS_CLOCK_RATE: args.opus_clock_rate = atoi(pj_optarg); From 3accf04a42662f8146caf7cc89ad56175883bc55 Mon Sep 17 00:00:00 2001 From: Riza Sulistyo Date: Mon, 16 Dec 2024 11:37:23 +0700 Subject: [PATCH 139/491] Fix the info_cnt of pjsip_dlg_event_status when parsing (#4214) --- pjsip/include/pjsip-simple/dlg_event.h | 2 +- pjsip/src/pjsip-simple/dlg_event.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pjsip/include/pjsip-simple/dlg_event.h b/pjsip/include/pjsip-simple/dlg_event.h index 5f66a8a3a2..000022e0de 100644 --- a/pjsip/include/pjsip-simple/dlg_event.h +++ b/pjsip/include/pjsip-simple/dlg_event.h @@ -70,7 +70,7 @@ PJ_DECL(pjsip_module*) pjsip_bdlg_event_instance(void); * Maximum dialog event status info items which can handled by application. * */ -#define PJSIP_DLG_EVENT_STATUS_MAX_INFO 8 +#define PJSIP_DLG_EVENT_STATUS_MAX_INFO 1 /** diff --git a/pjsip/src/pjsip-simple/dlg_event.c b/pjsip/src/pjsip-simple/dlg_event.c index aaea3a14de..9b998633e0 100644 --- a/pjsip/src/pjsip-simple/dlg_event.c +++ b/pjsip/src/pjsip-simple/dlg_event.c @@ -332,12 +332,11 @@ pjsip_dlg_event_parse_dialog_info2(char *body, unsigned body_len, pjsip_dlg_info_dialog_info *dialog_info; pjsip_dlg_info_dialog *dialog; + dlgev_st->info_cnt = 0; dialog_info = pjsip_dlg_info_parse(pool, body, body_len); if (dialog_info == NULL) return PJSIP_SIMPLE_EBADPIDF; - dlgev_st->info_cnt = 0; - dialog = pjsip_dlg_info_dialog_info_get_dialog(dialog_info); pj_strdup(pool, &dlgev_st->info[dlgev_st->info_cnt].dialog_info_entity, pjsip_dlg_info_dialog_info_get_entity(dialog_info)); @@ -402,6 +401,7 @@ pjsip_dlg_event_parse_dialog_info2(char *body, unsigned body_len, } else { dlgev_st->info[dlgev_st->info_cnt].dialog_node = NULL; } + dlgev_st->info_cnt++; return PJ_SUCCESS; } From 53932c595e8973cda7150c9eac83dba91b27af49 Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 18 Dec 2024 09:21:48 +0800 Subject: [PATCH 140/491] Revert "Fixed warnings of duplicate libs (#4177)" (#4219) This reverts commit 8117bc593929b50f5f55402eae68b0bd7e4350ef. --- aconfigure | 5 +++-- aconfigure.ac | 5 +++-- build.mak.in | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/aconfigure b/aconfigure index 8714b57bd6..b5d3446319 100755 --- a/aconfigure +++ b/aconfigure @@ -7064,7 +7064,7 @@ if ac_fn_c_try_link "$LINENO" then : CFLAGS="$CFLAGS -DPJNATH_HAS_UPNP=1 $UPNP_CFLAGS" - LDFLAGS="$LDFLAGS $UPNP_LDFLAGS" + LDFLAGS="$LDFLAGS $UPNP_LDFLAGS $UPNP_LIBS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } @@ -8566,6 +8566,7 @@ printf "%s\n" "not found" >&6; } ac_sdl_cflags="-DPJMEDIA_VIDEO_DEV_HAS_SDL=1 $ac_sdl_cflags" ac_sdl_ldflags=`$SDL_CONFIG --libs` ac_sdl_ldflags=`echo "${ac_sdl_ldflags}" | sed -e 's/-mwindows//g'` + LIBS="$LIBS $ac_sdl_ldflags" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Unsupported SDL version" >&5 printf "%s\n" "Unsupported SDL version" >&6; } @@ -9230,7 +9231,7 @@ if ac_fn_c_try_link "$LINENO" then : ac_vpx_cflags="-DPJMEDIA_HAS_VPX_CODEC=1 $VPX_CFLAGS" - ac_vpx_ldflags="$VPX_LDFLAGS" + ac_vpx_ldflags="$VPX_LDFLAGS $VPX_LIBS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } diff --git a/aconfigure.ac b/aconfigure.ac index e70dec807f..279870e9d6 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -750,7 +750,7 @@ AC_ARG_ENABLE(upnp, ], [ CFLAGS="$CFLAGS -DPJNATH_HAS_UPNP=1 $UPNP_CFLAGS" - LDFLAGS="$LDFLAGS $UPNP_LDFLAGS" + LDFLAGS="$LDFLAGS $UPNP_LDFLAGS $UPNP_LIBS" AC_MSG_RESULT(yes) ], [ @@ -1546,6 +1546,7 @@ AC_ARG_ENABLE(sdl, ac_sdl_cflags="-DPJMEDIA_VIDEO_DEV_HAS_SDL=1 $ac_sdl_cflags" ac_sdl_ldflags=`$SDL_CONFIG --libs` ac_sdl_ldflags=`echo "${ac_sdl_ldflags}" | sed -e 's/-mwindows//g'` + LIBS="$LIBS $ac_sdl_ldflags" else AC_MSG_RESULT([Unsupported SDL version]) fi @@ -1805,7 +1806,7 @@ AC_ARG_ENABLE(vpx, ], [ ac_vpx_cflags="-DPJMEDIA_HAS_VPX_CODEC=1 $VPX_CFLAGS" - ac_vpx_ldflags="$VPX_LDFLAGS" + ac_vpx_ldflags="$VPX_LDFLAGS $VPX_LIBS" AC_MSG_RESULT(yes) ], [ diff --git a/build.mak.in b/build.mak.in index 336cc6a272..3955a8930c 100644 --- a/build.mak.in +++ b/build.mak.in @@ -331,6 +331,7 @@ export APP_LDLIBS := $(PJSUA_LIB_LDLIB) \ $(PJLIB_LDLIB) \ @LIBS@ export APP_LDXXLIBS := $(PJSUA2_LIB_LDLIB) \ + -lstdc++ \ $(APP_LDLIBS) # Here are the variables to use if application is using the library From 995630d4f54ab6256f9869cfa681ccf89796020e Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 18 Dec 2024 09:52:50 +0800 Subject: [PATCH 141/491] Fixed AEC algorithm selection (#4220) --- pjmedia/src/pjmedia/echo_common.c | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/pjmedia/src/pjmedia/echo_common.c b/pjmedia/src/pjmedia/echo_common.c index d5b84ea466..ce4e41a363 100644 --- a/pjmedia/src/pjmedia/echo_common.c +++ b/pjmedia/src/pjmedia/echo_common.c @@ -113,21 +113,6 @@ static struct ec_operations speex_aec_op = }; #endif - -/* - * IPP AEC prototypes - */ -#if defined(PJMEDIA_HAS_INTEL_IPP_AEC) && PJMEDIA_HAS_INTEL_IPP_AEC!=0 -static struct ec_operations ipp_aec_op = -{ - "IPP AEC", - &ipp_aec_create, - &ipp_aec_destroy, - &ipp_aec_reset, - &ipp_aec_cancel_echo -}; -#endif - /* * WebRTC AEC prototypes */ @@ -170,6 +155,7 @@ PJ_DEF(void) pjmedia_echo_stat_default(pjmedia_echo_stat *stat) stat->return_loss_enh = (double)PJMEDIA_ECHO_STAT_NOT_SPECIFIED; stat->std = PJMEDIA_ECHO_STAT_NOT_SPECIFIED; stat->frac_delay = (float)PJMEDIA_ECHO_STAT_NOT_SPECIFIED; + stat->learning = PJMEDIA_ECHO_STAT_NOT_SPECIFIED; stat->duration = PJMEDIA_ECHO_STAT_NOT_SPECIFIED; stat->tail = PJMEDIA_ECHO_STAT_NOT_SPECIFIED; stat->min_factor = PJMEDIA_ECHO_STAT_NOT_SPECIFIED; @@ -229,14 +215,6 @@ PJ_DEF(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool, ec->op = &speex_aec_op; #endif -#if defined(PJMEDIA_HAS_INTEL_IPP_AEC) && PJMEDIA_HAS_INTEL_IPP_AEC!=0 - } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_IPP || - (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT) - { - ec->op = &ipp_aec_op; - -#endif - #if defined(PJMEDIA_HAS_WEBRTC_AEC) && PJMEDIA_HAS_WEBRTC_AEC!=0 } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_WEBRTC || (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT) @@ -251,8 +229,13 @@ PJ_DEF(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool, ec->op = &webrtc_aec3_op; #endif - } else { + } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_SIMPLE || + (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT) + { ec->op = &echo_supp_op; + + } else { + return PJ_ENOTSUP; } /* Completeness check for EC operation playback and capture, they must From 2faf4284806afd08ba57c45eb73a1c02e82593f9 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Wed, 18 Dec 2024 11:24:52 +0700 Subject: [PATCH 142/491] Fixed port double destruction in mips_test() and include benchmark tests in pj/config_site_test.h (#4221) --- pjlib/include/pj/config_site_test.h | 2 +- pjmedia/src/test/mips_test.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pjlib/include/pj/config_site_test.h b/pjlib/include/pj/config_site_test.h index d8bc9595ee..8fb2599d66 100644 --- a/pjlib/include/pj/config_site_test.h +++ b/pjlib/include/pj/config_site_test.h @@ -8,4 +8,4 @@ #define PJMEDIA_CODEC_L16_HAS_48KHZ_STEREO 1 #define PJMEDIA_HAS_G7221_CODEC 1 #define PJMEDIA_HAS_G722_CODEC 1 -#define PJ_EXCLUDE_BENCHMARK_TESTS 1 +#define PJ_EXCLUDE_BENCHMARK_TESTS 0 diff --git a/pjmedia/src/test/mips_test.c b/pjmedia/src/test/mips_test.c index d1518fc41d..25f89bfcbe 100644 --- a/pjmedia/src/test/mips_test.c +++ b/pjmedia/src/test/mips_test.c @@ -2388,8 +2388,8 @@ static pj_timestamp run_entry(unsigned clock_rate, struct test_entry *e) if (e->custom_deinit) e->custom_deinit(e); - - pjmedia_port_destroy(port); + else + pjmedia_port_destroy(port); pj_pool_release(pool); return t1; From ea944b9e15ca34cf2e5df04e3d838e8895d1e210 Mon Sep 17 00:00:00 2001 From: LeonidGoltsblat <138720759+LeonidGoltsblat@users.noreply.github.com> Date: Wed, 18 Dec 2024 08:31:02 +0100 Subject: [PATCH 143/491] VS build system files fixes (#4212) --- build/vs/pjproject-vs14-common-config.props | 6 +- pjlib/build/pjlib.vcxproj | 26 ++++ pjlib/build/pjlib.vcxproj.filters | 6 + pjproject-vs14.sln | 20 +-- pjsip-apps/build/pjsystest.vcxproj | 151 ++++++++++++++++++++ 5 files changed, 196 insertions(+), 13 deletions(-) diff --git a/build/vs/pjproject-vs14-common-config.props b/build/vs/pjproject-vs14-common-config.props index 456e4f02e1..840bf93e34 100644 --- a/build/vs/pjproject-vs14-common-config.props +++ b/build/vs/pjproject-vs14-common-config.props @@ -23,15 +23,15 @@ - v140 + v143 WIN32;PJ_WIN32=1;PJ_M_I386=1; WIN64;PJ_WIN64=1;PJ_M_X86_64=1; - PJ_M_ARM64=1; + PJ_M_ARM64=1; - v140 + v143 PJ_WIN32_UWP;UNICODE;_UNICODE; $(PreprocessorDef);PJ_M_ARMV7=1; 10.0.10586.0 diff --git a/pjlib/build/pjlib.vcxproj b/pjlib/build/pjlib.vcxproj index 4467f52e20..6074880f6f 100644 --- a/pjlib/build/pjlib.vcxproj +++ b/pjlib/build/pjlib.vcxproj @@ -899,6 +899,32 @@ + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true diff --git a/pjlib/build/pjlib.vcxproj.filters b/pjlib/build/pjlib.vcxproj.filters index 2889b4666e..a44f9bd7e6 100644 --- a/pjlib/build/pjlib.vcxproj.filters +++ b/pjlib/build/pjlib.vcxproj.filters @@ -224,6 +224,12 @@ Source Files + + Source Files + + + Source Files + diff --git a/pjproject-vs14.sln b/pjproject-vs14.sln index fcd0926d70..7ec360db4c 100644 --- a/pjproject-vs14.sln +++ b/pjproject-vs14.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31624.102 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35327.3 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1DFF1CF3-DBD7-4DA4-A36D-663D695EB678}" ProjectSection(SolutionItems) = preProject @@ -121,10 +121,6 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimplePjsua2CS", "pjsip-apps\src\swig\csharp\sample\SimplePjsua2CS\SimplePjsua2CS.csproj", "{3DCDD02D-C3CF-40EB-A66C-F7C2DAD93FD9}" EndProject Global - GlobalSection(SharedMSBuildProjectFiles) = preSolution - pjsip-apps\src\pjsua\winrt\cli\comp\pjsua_cli_shared_comp.vcxitems*{207e7bd4-7b11-4a40-ba3a-cc627762a7b6}*SharedItemsImports = 4 - pjsip-apps\src\pjsua\winrt\cli\comp\pjsua_cli_shared_comp.vcxitems*{45d41acc-2c3c-43d2-bc10-02aa73ffc7c7}*SharedItemsImports = 9 - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|ARM = Debug|ARM @@ -1571,8 +1567,8 @@ Global {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Debug-Dynamic|ARM.ActiveCfg = Debug|Win32 {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Debug-Dynamic|ARM64.ActiveCfg = Debug|ARM64 {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Debug-Dynamic|ARM64.Build.0 = Debug|ARM64 - {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Debug-Dynamic|Win32.ActiveCfg = Debug|Win32 - {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Debug-Dynamic|Win32.Build.0 = Debug|Win32 + {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Debug-Dynamic|Win32.ActiveCfg = Debug-Dynamic|Win32 + {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Debug-Dynamic|Win32.Build.0 = Debug-Dynamic|Win32 {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Debug-Dynamic|x64.ActiveCfg = Debug|x64 {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Debug-Dynamic|x64.Build.0 = Debug|x64 {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Debug-Static|Any CPU.ActiveCfg = Release|x64 @@ -1597,8 +1593,8 @@ Global {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Release-Dynamic|ARM.ActiveCfg = Release|Win32 {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Release-Dynamic|ARM64.ActiveCfg = Release|ARM64 {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Release-Dynamic|ARM64.Build.0 = Release|ARM64 - {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Release-Dynamic|Win32.ActiveCfg = Release|Win32 - {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Release-Dynamic|Win32.Build.0 = Release|Win32 + {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Release-Dynamic|Win32.ActiveCfg = Release-Dynamic|Win32 + {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Release-Dynamic|Win32.Build.0 = Release-Dynamic|Win32 {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Release-Dynamic|x64.ActiveCfg = Release|x64 {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Release-Dynamic|x64.Build.0 = Release|x64 {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Release-Static|Any CPU.ActiveCfg = Release|x64 @@ -2525,4 +2521,8 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9D1363FE-B481-479E-82A2-290F6C4DB91B} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + pjsip-apps\src\pjsua\winrt\cli\comp\pjsua_cli_shared_comp.vcxitems*{207e7bd4-7b11-4a40-ba3a-cc627762a7b6}*SharedItemsImports = 4 + pjsip-apps\src\pjsua\winrt\cli\comp\pjsua_cli_shared_comp.vcxitems*{45d41acc-2c3c-43d2-bc10-02aa73ffc7c7}*SharedItemsImports = 9 + EndGlobalSection EndGlobal diff --git a/pjsip-apps/build/pjsystest.vcxproj b/pjsip-apps/build/pjsystest.vcxproj index 568b2ff816..624f8322a4 100644 --- a/pjsip-apps/build/pjsystest.vcxproj +++ b/pjsip-apps/build/pjsystest.vcxproj @@ -1,6 +1,18 @@  + + Debug-Dynamic + ARM64 + + + Debug-Dynamic + Win32 + + + Debug-Dynamic + x64 + Debug ARM64 @@ -13,6 +25,18 @@ Debug x64 + + Release-Dynamic + ARM64 + + + Release-Dynamic + Win32 + + + Release-Dynamic + x64 + Release ARM64 @@ -40,26 +64,50 @@ Application v140 + + Application + v140 + Application v140 + + Application + v140 + Application v140 + + Application + v140 + Application v140 + + Application + v140 + Application v140 + + Application + v140 + Application v140 + + Application + v140 + $(BuildToolset) @@ -76,31 +124,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.22823.1 @@ -120,6 +198,16 @@ Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies) + + + ../../pjsip/include;../../pjlib/include;../../pjlib-util/include;../../pjmedia/include;../../pjnath/include;%(AdditionalIncludeDirectories) + _CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + + + Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies) + + X64 @@ -134,6 +222,20 @@ MachineX64 + + + X64 + + + ../../pjsip/include;../../pjlib/include;../../pjlib-util/include;../../pjmedia/include;../../pjnath/include;%(AdditionalIncludeDirectories) + _CONSOLE;%(PreprocessorDefinitions) + ProgramDatabase + + + Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies) + MachineX64 + + @@ -145,6 +247,17 @@ Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies) + + + + ../../pjsip/include;../../pjlib/include;../../pjlib-util/include;../../pjmedia/include;../../pjnath/include;%(AdditionalIncludeDirectories) + _CONSOLE;%(PreprocessorDefinitions) + ProgramDatabase + + + Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies) + + ../../pjsip/include;../../pjlib/include;../../pjlib-util/include;../../pjmedia/include;../../pjnath/include;%(AdditionalIncludeDirectories) @@ -154,6 +267,15 @@ Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies) + + + ../../pjsip/include;../../pjlib/include;../../pjlib-util/include;../../pjmedia/include;../../pjnath/include;%(AdditionalIncludeDirectories) + _CONSOLE;%(PreprocessorDefinitions) + + + Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies) + + X64 @@ -167,6 +289,19 @@ MachineX64 + + + X64 + + + ../../pjsip/include;../../pjlib/include;../../pjlib-util/include;../../pjmedia/include;../../pjnath/include;%(AdditionalIncludeDirectories) + _CONSOLE;%(PreprocessorDefinitions) + + + Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies) + MachineX64 + + @@ -177,15 +312,31 @@ Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies) + + + + ../../pjsip/include;../../pjlib/include;../../pjlib-util/include;../../pjmedia/include;../../pjnath/include;%(AdditionalIncludeDirectories) + _CONSOLE;%(PreprocessorDefinitions) + + + Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies) + + true + true true + true true + true true + true true + true true + true From e85d82714bff355a7d98742913245fb74837f007 Mon Sep 17 00:00:00 2001 From: sauwming Date: Thu, 19 Dec 2024 12:50:46 +0800 Subject: [PATCH 144/491] Limit number of Oboe auto restart (#4222) --- pjmedia/src/pjmedia-audiodev/oboe_dev.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/pjmedia/src/pjmedia-audiodev/oboe_dev.cpp b/pjmedia/src/pjmedia-audiodev/oboe_dev.cpp index c0d8054202..ce80141a12 100644 --- a/pjmedia/src/pjmedia-audiodev/oboe_dev.cpp +++ b/pjmedia/src/pjmedia-audiodev/oboe_dev.cpp @@ -39,6 +39,9 @@ #include #include +/* Setting of maximum number of consecutive stream restart attempts. */ +#define MAX_RESTART 3 + /* Device info */ typedef struct aud_dev_info { @@ -416,7 +419,6 @@ static pj_status_t oboe_refresh(pjmedia_aud_dev_factory *ff) f->dev_count++; - on_skip_dev: jni_env->DeleteLocalRef(jdev_info); } @@ -530,7 +532,7 @@ class MyOboeEngine : oboe::AudioStreamDataCallback, public: MyOboeEngine(struct oboe_aud_stream *stream_, pjmedia_dir dir_) : stream(stream_), dir(dir_), oboe_stream(NULL), dir_st(NULL), - thread(NULL), thread_quit(PJ_TRUE), queue(NULL), + thread(NULL), thread_quit(PJ_TRUE), nrestart(0), queue(NULL), err_thread_registered(false), mutex(NULL) { pj_assert(dir == PJMEDIA_DIR_CAPTURE || dir == PJMEDIA_DIR_PLAYBACK); @@ -745,6 +747,7 @@ class MyOboeEngine : oboe::AudioStreamDataCallback, } } + this->nrestart = 0; sem_post(&sem); return (thread_quit? oboe::DataCallbackResult::Stop : @@ -775,12 +778,19 @@ class MyOboeEngine : oboe::AudioStreamDataCallback, pj_status_t status; PJ_LOG(3,(THIS_FILE, - "Oboe stream %s error (%d/%s), " - "trying to restart stream..", + "Oboe stream %s error (%d/%s)", dir_st, result, oboe::convertToText(result))); Stop(); - status = Start(); + + if (nrestart < MAX_RESTART) { + ++nrestart; + PJ_LOG(3, (THIS_FILE, "Trying to restart Oboe %s stream #%d", + dir_st, nrestart)); + status = Start(); + } else { + status = PJMEDIA_EAUD_SYSERR; + } if (status != PJ_SUCCESS) { pjmedia_event e; @@ -930,6 +940,7 @@ class MyOboeEngine : oboe::AudioStreamDataCallback, const char *dir_st; pj_thread_t *thread; volatile pj_bool_t thread_quit; + unsigned nrestart; sem_t sem; pj_atomic_queue_t *queue; pj_timestamp ts; From cb8536872d509115e25f553849431161687d1c1e Mon Sep 17 00:00:00 2001 From: Riza Sulistyo Date: Thu, 19 Dec 2024 11:58:19 +0700 Subject: [PATCH 145/491] Fix issues related to #3754 (#4223) - wrong callback on event: TSX_STATE when using dialog-event subscription - wrong method called to re-subscribe from timer callback --- pjsip/src/pjsua-lib/pjsua_pres.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c index 9299312c68..2ed8d359fd 100644 --- a/pjsip/src/pjsua-lib/pjsua_pres.c +++ b/pjsip/src/pjsua-lib/pjsua_pres.c @@ -1679,7 +1679,10 @@ static void buddy_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry) PJ_UNUSED_ARG(th); entry->id = PJ_FALSE; - pjsua_buddy_update_pres(buddy->index); + if (buddy->presence) + pjsua_buddy_update_pres(buddy->index); + else + pjsua_buddy_update_dlg_event(buddy->index); } /* Reschedule subscription refresh timer or terminate the subscription @@ -2137,7 +2140,7 @@ static void subscribe_buddy(pjsua_buddy_id buddy_id, pj_log_pop_indent(); return; } - + buddy->presence = presence; pjsua_process_msg_data(tdata, NULL); /* Send request. Note that if the send operation fails sync-ly, e.g: @@ -2164,7 +2167,6 @@ static void subscribe_buddy(pjsua_buddy_id buddy_id, return; } - buddy->presence = presence; pjsip_dlg_dec_lock(buddy->dlg); if (tmp_pool) pj_pool_release(tmp_pool); pj_log_pop_indent(); From dc3bb71627aac501e228b92bc2b37ff7b2592b2b Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 20 Dec 2024 22:30:08 +0800 Subject: [PATCH 146/491] Fixed WebRTC AEC3 alignment issue on Linux ARMv7 (#4228) --- pjmedia/src/pjmedia/echo_webrtc_aec3.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pjmedia/src/pjmedia/echo_webrtc_aec3.cpp b/pjmedia/src/pjmedia/echo_webrtc_aec3.cpp index 006094b9e9..0bef72d54f 100644 --- a/pjmedia/src/pjmedia/echo_webrtc_aec3.cpp +++ b/pjmedia/src/pjmedia/echo_webrtc_aec3.cpp @@ -75,8 +75,13 @@ PJ_DEF(pj_status_t) webrtc_aec3_create(pj_pool_t *pool, webrtc_ec *echo; *p_echo = NULL; - + +#if WEBRTC_LINUX == 1 && defined(WEBRTC_ARCH_ARM_V7) + /* Workaround to fix alignment trap issue on Linux ARMv7 machine. */ + echo = new webrtc_ec(); +#else echo = PJ_POOL_ZALLOC_T(pool, webrtc_ec); +#endif PJ_ASSERT_RETURN(echo != NULL, PJ_ENOMEM); if (clock_rate != 16000 && clock_rate != 32000 && clock_rate != 48000) { @@ -152,6 +157,10 @@ PJ_DEF(pj_status_t) webrtc_aec3_destroy(void *state ) echo->rend_buf = NULL; } +#if WEBRTC_LINUX == 1 && defined(WEBRTC_ARCH_ARM_V7) + delete echo; +#endif + return PJ_SUCCESS; } From e9060fb02fc73288945fbc8f34293bf0941d98ac Mon Sep 17 00:00:00 2001 From: sauwming Date: Mon, 23 Dec 2024 08:52:46 +0800 Subject: [PATCH 147/491] Fixed Buddy to use correct account during subscription (#4227) --- pjsip/include/pjsua-lib/pjsua.h | 19 ++++++++++++++++++- pjsip/include/pjsua-lib/pjsua_internal.h | 1 + pjsip/include/pjsua2/presence.hpp | 9 +++++++++ pjsip/src/pjsua-lib/pjsua_acc.c | 13 +++++++++++++ pjsip/src/pjsua-lib/pjsua_core.c | 1 + pjsip/src/pjsua-lib/pjsua_pres.c | 20 ++++++++++++++++++-- pjsip/src/pjsua2/account.cpp | 15 +++++++++++++-- pjsip/src/pjsua2/presence.cpp | 10 ++++++++-- 8 files changed, 81 insertions(+), 7 deletions(-) diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index b64d653f2e..1124bea0b2 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -6554,11 +6554,22 @@ typedef struct pjsua_buddy_config pj_bool_t subscribe_dlg_event; /** - * Specify arbitrary application data to be associated with with + * Specify arbitrary application data to be associated with * the buddy object. */ void *user_data; + /** + * Specify account to be associated with the buddy object. The account + * will be used for creating the subscription. + * + * IMPORTANT: Account must remain valid throughout the entire lifetime + * of the buddy object. + * + * Default: PJSUA_INVALID_ID (buddy is not associated to any account) + */ + pjsua_acc_id acc_id; + } pjsua_buddy_config; @@ -6603,6 +6614,12 @@ typedef struct pjsua_buddy_info */ pj_str_t uri; + /** + * The account ID associated with this buddy. If not associated + * with any account, the value will be PJSUA_INVALID_ID. + */ + pjsua_acc_id acc_id; + /** * Buddy's Contact, only available when presence subscription has * been established to the buddy. diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index ca38660b8b..4750eb7ffc 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -368,6 +368,7 @@ typedef struct pjsua_buddy { pj_pool_t *pool; /**< Pool for this buddy. */ unsigned index; /**< Buddy index. */ + pjsua_acc_id acc_id; /**< Account index. */ void *user_data; /**< Application data. */ pj_str_t uri; /**< Buddy URI. */ pj_str_t contact; /**< Contact learned from subscrp. */ diff --git a/pjsip/include/pjsua2/presence.hpp b/pjsip/include/pjsua2/presence.hpp index 5fc76f0400..d19cd66e53 100644 --- a/pjsip/include/pjsua2/presence.hpp +++ b/pjsip/include/pjsua2/presence.hpp @@ -127,6 +127,12 @@ struct BuddyInfo */ string contact; + /** + * The account ID associated with this buddy. If not associated + * with any account, the value will be PJSUA_INVALID_ID. + */ + pjsua_acc_id accId; + /** * Flag to indicate that we should monitor the presence information for * this buddy (normally yes, unless explicitly disabled). @@ -238,6 +244,9 @@ class Buddy * the instance that calls this create() method as it is only the original * instance destructor that will delete the underlying Buddy in PJSUA-LIB. * + * IMPORTANT: Application must make sure that the Account instance remains + * valid for the entire lifetime of the Buddy object. + * * @param acc The account for this buddy. * @param cfg The buddy config. */ diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index c66c54faff..bb6998d625 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -678,6 +678,19 @@ PJ_DEF(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_id) acc = &pjsua_var.acc[acc_id]; + for (i = 0; i < PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) { + pjsua_buddy *b = &pjsua_var.buddy[i]; + + if (!pjsua_buddy_is_valid(i)) + continue; + if (b->acc_id == acc_id) { + PJ_LOG(3, (THIS_FILE, "Warning: Account %d is used by " + "buddy %d. Disassociating it.", + acc_id, i)); + b->acc_id = PJSUA_INVALID_ID; + } + } + /* Cancel keep-alive timer, if any */ if (acc->ka_timer.id) { pjsip_endpt_cancel_timer(pjsua_var.endpt, &acc->ka_timer); diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 457738ef75..9f4cd232e3 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -390,6 +390,7 @@ PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg) PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg) { pj_bzero(cfg, sizeof(*cfg)); + cfg->acc_id = PJSUA_INVALID_ID; } PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg) diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c index 2ed8d359fd..bb5f72526c 100644 --- a/pjsip/src/pjsua-lib/pjsua_pres.c +++ b/pjsip/src/pjsua-lib/pjsua_pres.c @@ -246,6 +246,9 @@ PJ_DEF(pj_status_t) pjsua_buddy_get_info( pjsua_buddy_id buddy_id, pj_strncpy(&info->uri, &buddy->uri, sizeof(info->buf_)-total); total += info->uri.slen; + /* acc id */ + info->acc_id = buddy->acc_id; + /* contact */ if (total < sizeof(info->buf_)) { info->contact.ptr = info->buf_ + total; @@ -467,6 +470,7 @@ static void reset_buddy(pjsua_buddy_id id) pj_bzero(&pjsua_var.buddy[id], sizeof(pjsua_var.buddy[id])); pjsua_var.buddy[id].pool = pool; pjsua_var.buddy[id].index = id; + pjsua_var.buddy[id].acc_id = PJSUA_INVALID_ID; } @@ -560,6 +564,7 @@ PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg, pjsua_var.buddy[index].host = sip_uri->host; pjsua_var.buddy[index].port = sip_uri->port; pjsua_var.buddy[index].monitor = cfg->subscribe; + pjsua_var.buddy[index].acc_id = cfg->acc_id; if (pjsua_var.buddy[index].port == 0) pjsua_var.buddy[index].port = 5060; @@ -573,7 +578,12 @@ PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg, PJSUA_UNLOCK(); - PJ_LOG(4,(THIS_FILE, "Buddy %d added.", index)); + if (cfg->acc_id != PJSUA_INVALID_ID) { + PJ_LOG(4,(THIS_FILE, "Buddy %d added for account %d.", index, + cfg->acc_id)); + } else { + PJ_LOG(4,(THIS_FILE, "Buddy %d added.", index)); + } if (cfg->subscribe) { pjsua_buddy_subscribe_pres(index, cfg->subscribe); @@ -2013,7 +2023,13 @@ static void subscribe_buddy(pjsua_buddy_id buddy_id, dlg_event_callback.on_rx_notify = &pjsua_evsub_on_rx_dlg_event_notify; buddy = &pjsua_var.buddy[buddy_id]; - acc_id = pjsua_acc_find_for_outgoing(&buddy->uri); + acc_id = (buddy->acc_id != PJSUA_INVALID_ID)? buddy->acc_id: + pjsua_acc_find_for_outgoing(&buddy->uri); + if (!pjsua_acc_is_valid(acc_id)) { + PJ_LOG(4,(THIS_FILE, "Buddy %d: subscription failed, account %d is " + "invalid!", buddy_id, acc_id)); + return; + } acc = &pjsua_var.acc[acc_id]; diff --git a/pjsip/src/pjsua2/account.cpp b/pjsip/src/pjsua2/account.cpp index 7c65bfc038..9807b14115 100644 --- a/pjsip/src/pjsua2/account.cpp +++ b/pjsip/src/pjsua2/account.cpp @@ -1134,7 +1134,12 @@ BuddyVector2 Account::enumBuddies2() const PJSUA2_THROW(Error) PJSUA2_CHECK_EXPR( pjsua_enum_buddies(ids, &count) ); for (i = 0; i < count; ++i) { - bv2.push_back(Buddy(ids[i])); + pjsua_buddy_info pbi; + + pjsua_buddy_get_info(ids[i], &pbi); + if (id == pbi.acc_id) { + bv2.push_back(Buddy(ids[i])); + } } return bv2; @@ -1161,11 +1166,17 @@ Buddy Account::findBuddy2(string uri) const PJSUA2_THROW(Error) { pj_str_t pj_uri; pjsua_buddy_id bud_id; + pjsua_buddy_info pbi; pj_strset2(&pj_uri, (char*)uri.c_str()); bud_id = pjsua_buddy_find(&pj_uri); - if (id == PJSUA_INVALID_ID) { + if (bud_id == PJSUA_INVALID_ID) { + PJSUA2_RAISE_ERROR(PJ_ENOTFOUND); + } + + pjsua_buddy_get_info(bud_id, &pbi); + if (id != pbi.acc_id) { PJSUA2_RAISE_ERROR(PJ_ENOTFOUND); } diff --git a/pjsip/src/pjsua2/presence.cpp b/pjsip/src/pjsua2/presence.cpp index 364c19a3e7..7625a44264 100644 --- a/pjsip/src/pjsua2/presence.cpp +++ b/pjsip/src/pjsua2/presence.cpp @@ -57,6 +57,7 @@ void BuddyInfo::fromPj(const pjsua_buddy_info &pbi) { uri = pj2Str(pbi.uri); contact = pj2Str(pbi.contact); + accId = pbi.acc_id; presMonitorEnabled = PJ2BOOL(pbi.monitor_pres); subState = pbi.sub_state; subStateName = string(pbi.sub_state_name); @@ -113,9 +114,13 @@ Buddy::~Buddy() pjsua_buddy_set_user_data(id, NULL); pjsua_buddy_del(id); +#if !DEPRECATED_FOR_TICKET_2232 /* Remove from account buddy list */ if (acc) acc->removeBuddy(this); +#else + PJ_UNUSED_ARG(acc); +#endif } } @@ -138,6 +143,7 @@ void Buddy::create(Account &account, const BuddyConfig &cfg) pj_cfg.uri = str2Pj(cfg.uri); pj_cfg.subscribe = cfg.subscribe; pj_cfg.user_data = (void*)bud; + pj_cfg.acc_id = account.getId(); PJSUA2_CHECK_EXPR( pjsua_buddy_add(&pj_cfg, &id) ); account.addBuddy(this); @@ -196,7 +202,7 @@ void Buddy::sendInstantMessage(const SendInstantMessageParam &prm) BuddyUserData *bud = (BuddyUserData*)pjsua_buddy_get_user_data(id); Account *acc = bud? bud->acc : NULL; - if (!bud || !acc || !acc->isValid()) { + if (!bud || bi.accId == PJSUA_INVALID_ID || !acc || !acc->isValid()) { PJSUA2_RAISE_ERROR3(PJ_EINVAL, "sendInstantMessage()", "Invalid Buddy"); } @@ -222,7 +228,7 @@ void Buddy::sendTypingIndication(const SendTypingIndicationParam &prm) BuddyUserData *bud = (BuddyUserData*)pjsua_buddy_get_user_data(id); Account *acc = bud? bud->acc : NULL; - if (!bud || !acc || !acc->isValid()) { + if (!bud || bi.accId == PJSUA_INVALID_ID || !acc || !acc->isValid()) { PJSUA2_RAISE_ERROR3(PJ_EINVAL, "sendInstantMessage()", "Invalid Buddy"); } From e03d43808c9ba99e7ad932e30352a1b615dce179 Mon Sep 17 00:00:00 2001 From: LeonidGoltsblat <138720759+LeonidGoltsblat@users.noreply.github.com> Date: Wed, 25 Dec 2024 02:52:19 +0100 Subject: [PATCH 148/491] pjmedia_wav_player_port_create() fix (#4231) --- pjmedia/src/pjmedia/wav_player.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pjmedia/src/pjmedia/wav_player.c b/pjmedia/src/pjmedia/wav_player.c index 774ddda180..3a9b9d4fb8 100644 --- a/pjmedia/src/pjmedia/wav_player.c +++ b/pjmedia/src/pjmedia/wav_player.c @@ -88,10 +88,8 @@ static struct file_reader_port *create_file_port(pj_pool_t *pool) struct file_reader_port *port; port = PJ_POOL_ZALLOC_T(pool, struct file_reader_port); - if (!port) { - pj_pool_release(pool); + if (!port) return NULL; - } /* Put in default values. * These will be overriden once the file is read. From e16edeef9052dcefb224edefa6605e8cc159bcbe Mon Sep 17 00:00:00 2001 From: Perry Ismangil Date: Wed, 25 Dec 2024 02:11:46 +0000 Subject: [PATCH 149/491] Added Bitrise badges --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 642dcf72e7..012831e774 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![CI Linux](https://github.com/pjsip/pjproject/actions/workflows/ci-linux.yml/badge.svg)](https://github.com/pjsip/pjproject/actions/workflows/ci-linux.yml) [![CI Mac](https://github.com/pjsip/pjproject/actions/workflows/ci-mac.yml/badge.svg)](https://github.com/pjsip/pjproject/actions/workflows/ci-mac.yml) [![CI Windows](https://github.com/pjsip/pjproject/actions/workflows/ci-win.yml/badge.svg)](https://github.com/pjsip/pjproject/actions/workflows/ci-win.yml) +[![Bitrise iOS](https://img.shields.io/bitrise/70e79dc5-cae8-4cb7-a6cd-9a5bd3f3270f?token=tnXk2DZ71Zmd0qDMhFgiBg&label=CI%20iOS)](https://app.bitrise.io/app/70e79dc5-cae8-4cb7-a6cd-9a5bd3f3270f) +[![Bitrise Android](https://img.shields.io/bitrise/e4b6aade20ea9eb3?token=byZU0e1BJn_VYg2YuAs-cA&label=CI%20Android)](https://app.bitrise.io/app/e4b6aade20ea9eb3)
[![OSS-Fuzz](https://oss-fuzz-build-logs.storage.googleapis.com/badges/pjsip.png)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html#pjsip) [![Coverity-Scan](https://scan.coverity.com/projects/905/badge.svg)](https://scan.coverity.com/projects/pjsip) From 8bc04d9ea73c5c35abcc1093da24a73a9f0a360f Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 26 Dec 2024 08:32:51 +0900 Subject: [PATCH 150/491] Fix SSL to continue decrypting data after renego completes (#4234) * Fix SSL to continue decrypting data after renego completes * Update SSL socket with OpenSSL backend to return PJ_EEOF only when renegotiation is needed (or not completed). --- pjlib/src/pj/ssl_sock_imp_common.c | 5 +++++ pjlib/src/pj/ssl_sock_ossl.c | 12 ++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/pjlib/src/pj/ssl_sock_imp_common.c b/pjlib/src/pj/ssl_sock_imp_common.c index f1d1db63dd..811c960fd1 100644 --- a/pjlib/src/pj/ssl_sock_imp_common.c +++ b/pjlib/src/pj/ssl_sock_imp_common.c @@ -867,6 +867,11 @@ static pj_bool_t ssock_on_data_read (pj_ssl_sock_t *ssock, "Failed to flush delayed send")); goto on_error; } + + /* If renego has been completed, continue reading data */ + if (status == PJ_SUCCESS) + continue; + } else if (status != PJ_EPENDING) { PJ_PERROR(1,(ssock->pool->obj_name, status, "Renegotiation failed")); diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c index 67f386d00d..8524c43b79 100644 --- a/pjlib/src/pj/ssl_sock_ossl.c +++ b/pjlib/src/pj/ssl_sock_ossl.c @@ -2476,7 +2476,9 @@ static pj_status_t ssl_read(pj_ssl_sock_t *ssock, void *data, int *size) /* SSL might just return SSL_ERROR_WANT_READ in * re-negotiation. */ - if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ) { + if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ && + err != SSL_ERROR_ZERO_RETURN) + { if (err == SSL_ERROR_SYSCALL && size_ == -1 && ERR_peek_error() == 0 && errno == 0) { @@ -2499,9 +2501,11 @@ static pj_status_t ssl_read(pj_ssl_sock_t *ssock, void *data, int *size) } } - pj_lock_release(ssock->write_mutex); - /* Need renegotiation */ - return PJ_EEOF; + /* Return PJ_EEOF when SSL needs renegotiation */ + if (!SSL_is_init_finished(ossock->ossl_ssl)) { + pj_lock_release(ssock->write_mutex); + return PJ_EEOF; + } } pj_lock_release(ssock->write_mutex); From 653ff5774a627885748a37739e2503c1425303b9 Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 3 Jan 2025 12:53:08 +0800 Subject: [PATCH 151/491] Fixes GnuTLS unit test failures (#4236) --- pjlib/src/pj/ssl_sock_gtls.c | 40 ++++++++++++++++++++++++++++----- pjlib/src/pjlib-test/ssl_sock.c | 3 +++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/pjlib/src/pj/ssl_sock_gtls.c b/pjlib/src/pj/ssl_sock_gtls.c index 5beae67822..a08cf131c6 100644 --- a/pjlib/src/pj/ssl_sock_gtls.c +++ b/pjlib/src/pj/ssl_sock_gtls.c @@ -407,7 +407,10 @@ static pj_status_t tls_str_append_once(pj_str_t *dst, pj_str_t *src) if (dst->slen + src->slen + 3 > 1024) return PJ_ETOOMANY; - pj_strcat2(dst, ":+"); + if (dst->slen > 0 && dst->ptr[dst->slen-1] == ':') + pj_strcat2(dst, "+"); + else + pj_strcat2(dst, ":+"); pj_strcat(dst, src); } return PJ_SUCCESS; @@ -430,8 +433,16 @@ static pj_status_t tls_priorities_set(pj_ssl_sock_t *ssock) pj_strset(&cipher_list, buf, 0); pj_strset(&priority, priority_buf, 0); + if (ssock->param.proto == PJ_SSL_SOCK_PROTO_DEFAULT) + ssock->param.proto = PJ_SSL_SOCK_PROTO_TLS1 | + PJ_SSL_SOCK_PROTO_TLS1_1 | + PJ_SSL_SOCK_PROTO_TLS1_2; + /* For each level, enable only the requested protocol */ - pj_strcat2(&priority, "NORMAL:"); + pj_strcat2(&priority, "NORMAL:-VERS-ALL:"); + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_3) { + pj_strcat2(&priority, "+VERS-TLS1.3:"); + } if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_2) { pj_strcat2(&priority, "+VERS-TLS1.2:"); } @@ -441,10 +452,17 @@ static pj_status_t tls_priorities_set(pj_ssl_sock_t *ssock) if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1) { pj_strcat2(&priority, "+VERS-TLS1.0:"); } - pj_strcat2(&priority, "-VERS-SSL3.0:"); - pj_strcat2(&priority, "%LATEST_RECORD_VERSION"); + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_SSL3) { + pj_strcat2(&priority, "+VERS-SSL3.0:"); + } + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_SSL2) { + /* GnuTLS doesn't support SSLv2 */ + } pj_strcat(&cipher_list, &priority); + if (ssock->param.ciphers_num > 0) { + pj_strcat2(&cipher_list, "-CIPHER-ALL"); + } for (i = 0; i < (int)ssock->param.ciphers_num; i++) { for (j = 0; ; j++) { pj_ssl_cipher c; @@ -466,12 +484,17 @@ static pj_status_t tls_priorities_set(pj_ssl_sock_t *ssock) pj_str_t cipher_entry; /* Protocol version */ + /* We shouldn't add protocol support that's not specified + * in ssock->param.proto. + */ + /* pj_strset(&cipher_entry, temp, 0); pj_strcat2(&cipher_entry, "VERS-"); pj_strcat2(&cipher_entry, gnutls_protocol_get_name(proto)); ret = tls_str_append_once(&cipher_list, &cipher_entry); if (ret != PJ_SUCCESS) return ret; + */ /* Cipher */ pj_strset(&cipher_entry, temp, 0); @@ -513,6 +536,8 @@ static pj_status_t tls_priorities_set(pj_ssl_sock_t *ssock) } /* End the string and print it */ + if (cipher_list.ptr[cipher_list.slen-1] == ':') + cipher_list.slen--; cipher_list.ptr[cipher_list.slen] = '\0'; PJ_LOG(5, (ssock->pool->obj_name, "Priority string: %s", cipher_list.ptr)); @@ -623,6 +648,10 @@ static pj_status_t ssl_create(pj_ssl_sock_t *ssock) pj_status_t status; int ret; + /* Suppress warnings */ + PJ_UNUSED_ARG(circ_reset); + PJ_UNUSED_ARG(circ_read_cancel); + pj_assert(ssock); cert = ssock->cert; @@ -802,7 +831,8 @@ static void ssl_destroy(pj_ssl_sock_t *ssock) gnutls_sock_t *gssock = (gnutls_sock_t *)ssock; if (gssock->session) { - gnutls_bye(gssock->session, GNUTLS_SHUT_RDWR); + if (ssock->ssl_state == SSL_STATE_ESTABLISHED) + gnutls_bye(gssock->session, GNUTLS_SHUT_RDWR); gnutls_deinit(gssock->session); gssock->session = NULL; } diff --git a/pjlib/src/pjlib-test/ssl_sock.c b/pjlib/src/pjlib-test/ssl_sock.c index a6d1fae0e4..bc959499da 100644 --- a/pjlib/src/pjlib-test/ssl_sock.c +++ b/pjlib/src/pjlib-test/ssl_sock.c @@ -1644,12 +1644,15 @@ int ssl_sock_test(void) if (ret != 0) return ret; + /* SSLv23 is deprecated */ + /* PJ_LOG(3,("", "..echo test w/ SSLv23 and PJ_TLS_RSA_WITH_AES_256_CBC_SHA cipher")); ret = echo_test(PJ_SSL_SOCK_PROTO_SSL23, PJ_SSL_SOCK_PROTO_SSL23, PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_FALSE, PJ_FALSE); if (ret != 0) return ret; + */ #endif PJ_LOG(3,("", "..echo test w/ compatible proto: server TLSv1.2 vs client TLSv1.2")); From 536df27c9239308d0f6b6f3d4429919bd0600184 Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 3 Jan 2025 16:06:16 +0800 Subject: [PATCH 152/491] Only enable DTLS if SSL backend is OpenSSL (#4239) --- pjmedia/src/pjmedia/transport_srtp.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pjmedia/src/pjmedia/transport_srtp.c b/pjmedia/src/pjmedia/transport_srtp.c index fd32a7b63b..033083f782 100644 --- a/pjmedia/src/pjmedia/transport_srtp.c +++ b/pjmedia/src/pjmedia/transport_srtp.c @@ -31,6 +31,15 @@ #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) +/* Currently SRTP-DTLS requires OpenSSL */ +#if PJMEDIA_SRTP_HAS_DTLS +# if PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_OPENSSL +# pragma message("DTLS requires OpenSSL, disabling it...") +# undef PJMEDIA_SRTP_HAS_DTLS +# define PJMEDIA_SRTP_HAS_DTLS 0 +# endif +#endif + /* Enable this to test ROC initialization setting. For offerer, * it will send packets with ROC 1 and expect to receive ROC 2. * For answerer it will be the other way around. From 709ecdc9bccffb15dced05813dd25c5a1f8899cc Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 7 Jan 2025 09:22:15 +0700 Subject: [PATCH 153/491] Fix possible deadlock in conference (audio & video) (#4243) --- pjmedia/src/pjmedia/conference.c | 50 +++++++++++++++++++--------- pjmedia/src/pjmedia/vid_conf.c | 56 ++++++++++++++++++++++---------- 2 files changed, 74 insertions(+), 32 deletions(-) diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c index 80bfa66c84..0e6277a219 100644 --- a/pjmedia/src/pjmedia/conference.c +++ b/pjmedia/src/pjmedia/conference.c @@ -340,34 +340,53 @@ static op_entry* get_free_op_entry(pjmedia_conf *conf) static void handle_op_queue(pjmedia_conf *conf) { - op_entry *op, *next_op; + /* The queue may grow while mutex is released, better put a limit? */ + enum { MAX_PROCESSED_OP = 100 }; + int i = 0; - op = conf->op_queue->next; - while (op != conf->op_queue) { - next_op = op->next; + while (i++ < MAX_PROCESSED_OP) { + op_entry *op; + op_type type; + op_param param; + + pj_mutex_lock(conf->mutex); + + /* Stop when queue empty */ + if (pj_list_empty(conf->op_queue)) { + pj_mutex_unlock(conf->mutex); + break; + } + + /* Copy op */ + op = conf->op_queue->next; + type = op->type; + param = op->param; + + /* Free op */ pj_list_erase(op); + op->type = OP_UNKNOWN; + pj_list_push_back(conf->op_queue_free, op); - switch(op->type) { + pj_mutex_unlock(conf->mutex); + + /* Process op */ + switch(type) { case OP_ADD_PORT: - op_add_port(conf, &op->param); + op_add_port(conf, ¶m); break; case OP_REMOVE_PORT: - op_remove_port(conf, &op->param); + op_remove_port(conf, ¶m); break; case OP_CONNECT_PORTS: - op_connect_ports(conf, &op->param); + op_connect_ports(conf, ¶m); break; case OP_DISCONNECT_PORTS: - op_disconnect_ports(conf, &op->param); + op_disconnect_ports(conf, ¶m); break; default: pj_assert(!"Invalid sync-op in conference"); break; } - - op->type = OP_UNKNOWN; - pj_list_push_back(conf->op_queue_free, op); - op = next_op; } } @@ -1722,7 +1741,10 @@ static void op_remove_port(pjmedia_conf *conf, const op_param *prm) } /* Remove the port. */ + pj_mutex_lock(conf->mutex); conf->ports[port] = NULL; + pj_mutex_unlock(conf->mutex); + if (!conf_port->is_new) --conf->port_cnt; @@ -2418,9 +2440,7 @@ static pj_status_t get_frame(pjmedia_port *this_port, */ if (!pj_list_empty(conf->op_queue)) { pj_log_push_indent(); - pj_mutex_lock(conf->mutex); handle_op_queue(conf); - pj_mutex_unlock(conf->mutex); pj_log_pop_indent(); } diff --git a/pjmedia/src/pjmedia/vid_conf.c b/pjmedia/src/pjmedia/vid_conf.c index 9634c39267..81ae52501c 100644 --- a/pjmedia/src/pjmedia/vid_conf.c +++ b/pjmedia/src/pjmedia/vid_conf.c @@ -202,37 +202,56 @@ static op_entry* get_free_op_entry(pjmedia_vid_conf *conf) static void handle_op_queue(pjmedia_vid_conf *conf) { - op_entry *op, *next_op; - - op = conf->op_queue->next; - while (op != conf->op_queue) { - next_op = op->next; + /* The queue may grow while mutex is released, better put a limit? */ + enum { MAX_PROCESSED_OP = 100 }; + int i = 0; + + while (i++ < MAX_PROCESSED_OP) { + op_entry* op; + op_type type; + op_param param; + + pj_mutex_lock(conf->mutex); + + /* Stop when queue empty */ + if (pj_list_empty(conf->op_queue)) { + pj_mutex_unlock(conf->mutex); + break; + } + + /* Copy op */ + op = conf->op_queue->next; + type = op->type; + param = op->param; + + /* Free op */ pj_list_erase(op); + op->type = OP_UNKNOWN; + pj_list_push_back(conf->op_queue_free, op); + + pj_mutex_unlock(conf->mutex); - switch(op->type) { + /* Process op */ + switch (type) { case OP_ADD_PORT: - op_add_port(conf, &op->param); + op_add_port(conf, ¶m); break; case OP_REMOVE_PORT: - op_remove_port(conf, &op->param); + op_remove_port(conf, ¶m); break; case OP_CONNECT_PORTS: - op_connect_ports(conf, &op->param); + op_connect_ports(conf, ¶m); break; case OP_DISCONNECT_PORTS: - op_disconnect_ports(conf, &op->param); + op_disconnect_ports(conf, ¶m); break; case OP_UPDATE_PORT: - op_update_port(conf, &op->param); + op_update_port(conf, ¶m); break; default: pj_assert(!"Invalid sync-op in video conference"); break; } - - op->type = OP_UNKNOWN; - pj_list_push_back(conf->op_queue_free, op); - op = next_op; } } @@ -708,7 +727,10 @@ static void op_remove_port(pjmedia_vid_conf *vid_conf, } /* Remove the port. */ + pj_mutex_lock(vid_conf->mutex); vid_conf->ports[slot] = NULL; + pj_mutex_unlock(vid_conf->mutex); + if (!cport->is_new) --vid_conf->port_cnt; @@ -1086,9 +1108,9 @@ static void on_clock_tick(const pj_timestamp *now, void *user_data) * the clock such as connect, disonnect, remove, update. */ if (!pj_list_empty(vid_conf->op_queue)) { - pj_mutex_lock(vid_conf->mutex); + pj_log_push_indent(); handle_op_queue(vid_conf); - pj_mutex_unlock(vid_conf->mutex); + pj_log_pop_indent(); } /* No mutex from this point! Otherwise it may cause deadlock as From 67db8defdb2004841cb515bb06b24c624caf43ed Mon Sep 17 00:00:00 2001 From: Soroush mohammadi Date: Wed, 8 Jan 2025 04:59:59 +0330 Subject: [PATCH 154/491] Add siprec extension: handle incoming INVITE with siprec (#4132) --- pjmedia/include/pjmedia/sdp.h | 12 + pjmedia/src/pjmedia/sdp.c | 15 ++ pjsip/build/Makefile | 2 +- pjsip/build/pjsip_ua.vcproj | 8 + pjsip/build/pjsip_ua.vcxproj | 2 + pjsip/build/pjsip_ua.vcxproj.filters | 6 + pjsip/include/pjsip-ua/sip_inv.h | 10 + pjsip/include/pjsip-ua/sip_siprec.h | 131 +++++++++++ pjsip/include/pjsip_ua.h | 1 + pjsip/include/pjsua-lib/pjsua.h | 43 ++++ pjsip/include/pjsua-lib/pjsua_internal.h | 1 + pjsip/include/pjsua2/account.hpp | 9 + pjsip/src/pjsip-ua/sip_inv.c | 13 +- pjsip/src/pjsip-ua/sip_siprec.c | 286 +++++++++++++++++++++++ pjsip/src/pjsua-lib/pjsua_acc.c | 3 + pjsip/src/pjsua-lib/pjsua_call.c | 42 ++++ pjsip/src/pjsua-lib/pjsua_core.c | 6 + pjsip/src/pjsua-lib/pjsua_media.c | 13 ++ pjsip/src/pjsua2/account.cpp | 4 + 19 files changed, 604 insertions(+), 3 deletions(-) create mode 100644 pjsip/include/pjsip-ua/sip_siprec.h create mode 100644 pjsip/src/pjsip-ua/sip_siprec.c diff --git a/pjmedia/include/pjmedia/sdp.h b/pjmedia/include/pjmedia/sdp.h index 6442745a87..e2e325438d 100644 --- a/pjmedia/include/pjmedia/sdp.h +++ b/pjmedia/include/pjmedia/sdp.h @@ -370,6 +370,18 @@ PJ_DECL(pjmedia_sdp_attr*) pjmedia_sdp_attr_create_ssrc(pj_pool_t *pool, const pj_str_t *cname); +/** + * Create a=label attribute. + * + * @param pool Pool to create the attribute. + * @param attr Attribute to create. + * + * @return SDP label attribute. + */ +PJ_DECL(pjmedia_sdp_attr*) pjmedia_sdp_attr_create_label(pj_pool_t *pool, + const pj_str_t *label_str); + + /* ************************************************************************** * SDP CONNECTION INFO **************************************************************************** diff --git a/pjmedia/src/pjmedia/sdp.c b/pjmedia/src/pjmedia/sdp.c index ffc0413040..11cefb9658 100644 --- a/pjmedia/src/pjmedia/sdp.c +++ b/pjmedia/src/pjmedia/sdp.c @@ -572,6 +572,21 @@ PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_create_ssrc( pj_pool_t *pool, } +PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_create_label(pj_pool_t *pool, + const pj_str_t *label_str) +{ + pjmedia_sdp_attr *attr; + + attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr); + attr->name = pj_str("label"); + attr->value.ptr = (char *)pj_pool_alloc(pool, label_str->slen); + pj_memcpy(attr->value.ptr, label_str->ptr, label_str->slen); + attr->value.slen = label_str->slen; + + return attr; +} + + PJ_DEF(pj_status_t) pjmedia_sdp_attr_to_rtpmap(pj_pool_t *pool, const pjmedia_sdp_attr *attr, pjmedia_sdp_rtpmap **p_rtpmap) diff --git a/pjsip/build/Makefile b/pjsip/build/Makefile index 4c107295db..3238446708 100644 --- a/pjsip/build/Makefile +++ b/pjsip/build/Makefile @@ -80,7 +80,7 @@ export PJSIP_LDFLAGS += $(PJLIB_UTIL_LDLIB) \ export PJSIP_UA_SRCDIR = ../src/pjsip-ua export PJSIP_UA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ sip_inv.o sip_reg.o sip_replaces.o sip_xfer.o \ - sip_100rel.o sip_timer.o + sip_100rel.o sip_timer.o sip_siprec.o export PJSIP_UA_CFLAGS += $(_CFLAGS) export PJSIP_UA_CXXFLAGS += $(_CXXFLAGS) export PJSIP_UA_LDFLAGS += $(PJSIP_SIMPLE_LDLIB) \ diff --git a/pjsip/build/pjsip_ua.vcproj b/pjsip/build/pjsip_ua.vcproj index 74c9b8d8e8..c78a10a89b 100644 --- a/pjsip/build/pjsip_ua.vcproj +++ b/pjsip/build/pjsip_ua.vcproj @@ -3468,6 +3468,10 @@ RelativePath="..\src\pjsip-ua\sip_timer.c" >
+ + @@ -3609,6 +3613,10 @@ RelativePath="..\include\pjsip-ua\sip_timer.h" > + + diff --git a/pjsip/build/pjsip_ua.vcxproj b/pjsip/build/pjsip_ua.vcxproj index 2b08150ca2..379b571dd8 100644 --- a/pjsip/build/pjsip_ua.vcxproj +++ b/pjsip/build/pjsip_ua.vcxproj @@ -679,6 +679,7 @@ + @@ -687,6 +688,7 @@ + diff --git a/pjsip/build/pjsip_ua.vcxproj.filters b/pjsip/build/pjsip_ua.vcxproj.filters index fb4c6c200f..9c4c6c0a6e 100644 --- a/pjsip/build/pjsip_ua.vcxproj.filters +++ b/pjsip/build/pjsip_ua.vcxproj.filters @@ -26,6 +26,9 @@ Source Files + + Source Files + Source Files @@ -49,6 +52,9 @@ Header Files + + Header Files + Header Files diff --git a/pjsip/include/pjsip-ua/sip_inv.h b/pjsip/include/pjsip-ua/sip_inv.h index bb0b1ff020..0d0600cfa2 100644 --- a/pjsip/include/pjsip-ua/sip_inv.h +++ b/pjsip/include/pjsip-ua/sip_inv.h @@ -388,7 +388,17 @@ enum pjsip_inv_option * Require trickle ICE support. */ PJSIP_INV_REQUIRE_TRICKLE_ICE = 512, + + /** + * Require siprec support. + */ + PJSIP_INV_REQUIRE_SIPREC = 1024, + /** + * Indicate support for siprec + */ + PJSIP_INV_SUPPORT_SIPREC = 2048, + }; /* Forward declaration of Session Timers */ diff --git a/pjsip/include/pjsip-ua/sip_siprec.h b/pjsip/include/pjsip-ua/sip_siprec.h new file mode 100644 index 0000000000..a44b04abc6 --- /dev/null +++ b/pjsip/include/pjsip-ua/sip_siprec.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2024 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2024 Green and Silver Leaves. (https://github.com/BSVN) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIPREC_H__ +#define __PJSIP_SIPREC_H__ + +/** + * @file sip_siprec.h + * @brief SIP Session Recording Protocol (siprec) + * support (RFC 7866 - Session Recording Protocol in SIP) + */ + + +#include +#include + +/** + * @defgroup PJSIP_SIPREC SIP Session Recording Protocol (siprec) + * support (RFC 7866 - Session Recording Protocol in SIP) + * @brief SIP Session Recording Protocol support + * (RFC 7866 - Session Recording Protocol in SIP) + * @{ + * + * \section PJSIP_SIPREC_REFERENCE References + * + * References: + * - + * RFC 7866: Session Recording Protocol (siprec) + * in the Session Initiation Protocol (SIP) + */ +PJ_BEGIN_DECL + +/** + * Initialize siprec module. This function must be called once during + * application initialization, to register siprec module to SIP endpoint. + * + * @param endpt The SIP endpoint instance. + * + * @return PJ_SUCCESS if module is successfully initialized. + */ +PJ_DECL(pj_status_t) pjsip_siprec_init_module(pjsip_endpoint *endpt); + + +/** + * Check if the value of Require header is equal to siprec. + * + * @param req_hdr Require header. + * + * @return PJ_TRUE if value of Require header is equal to siprec. + */ +PJ_DECL(pj_status_t) +pjsip_siprec_verify_require_hdr(pjsip_require_hdr *req_hdr); + + +/** + * Verifies that the incoming request has the siprec value + * in the Require header and "+sip.src" parameter exist in the Contact header. + * If both conditions are met, according to RFC 7866, + * the INVITE request is a siprec. Otherwise, + * no changes are made to the request. if INVITE request is a siprec + * must have media attribute label exist in the SDP + * + * @param rdata The incoming request to be verified. + * @param metadata The siprec metadata information + * @param sdp_offer The SDP media. + * @param options The options argument is bitmask combination of SIP + * features in pjsip_inv_option enumeration + * @param dlg The dialog instance. + * @param endpt Media endpoint instance. + * @param p_tdata Upon error, it will be filled with the final response + * to be sent to the request sender. + * + * @return The function returns the following: + * - If the request includes the value siprec in the Require header + * and also includes "+sip.src" in the Contact header. + * PJ_SUCCESS and set PJSIP_INV_REQUIRE_SIPREC to options + * - Upon error condition (as described by RFC 7866), the + * function returns non-PJ_SUCCESS, and \a p_tdata + * parameter SHOULD be set with a final response message + * to be sent to the sender of the request. + */ +PJ_DECL(pj_status_t) pjsip_siprec_verify_request(pjsip_rx_data *rdata, + pj_str_t *metadata, + pjmedia_sdp_session *sdp_offer, + unsigned *options, + pjsip_dialog *dlg, + pjsip_endpoint *endpt, + pjsip_tx_data **p_tdata); + + +/** + * Find siprec metadata information from the message body + * with "rs-metadata" Content-Type. + * + * @param pool Pool to allocate memory. + * @param body The message body. + * @param metadata If metadata is found, this variable will be + * populated with the extracted data. + * + * @return Return PJ_SUCCESS if metadata is found, + * otherwise return PJ_ENOTFOUND. + */ +PJ_DECL(pj_status_t) pjsip_siprec_get_metadata(pj_pool_t *pool, + pjsip_msg_body *body, + pj_str_t* metadata); + + +PJ_END_DECL + + +/** + * @} + */ + + +#endif /* __PJSIP_SIPREC_H__ */ \ No newline at end of file diff --git a/pjsip/include/pjsip_ua.h b/pjsip/include/pjsip_ua.h index 234983a117..d79b61ebd4 100644 --- a/pjsip/include/pjsip_ua.h +++ b/pjsip/include/pjsip_ua.h @@ -25,6 +25,7 @@ #include #include #include +#include #endif /* __PJSIP_UA_H__ */ diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 1124bea0b2..7feb9fc821 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -2140,6 +2140,32 @@ typedef enum pjsua_sip_timer_use } pjsua_sip_timer_use; +/** + * This enumeration specifies the usage of SIPREC extension. + */ +typedef enum pjsua_sip_siprec_use +{ + /** + * When this flag is specified, when a SIPREC request is received, it + * returns bad extension error. and SIPREC calls will not be established. + */ + PJSUA_SIP_SIPREC_INACTIVE, + + /** + * When this flag is specified, when you want both regular calls and + * SIPREC calls to be established. + */ + PJSUA_SIP_SIPREC_OPTIONAL, + + /** + * When this flag is specified, when you want only SIPREC calls to + * be established, and regular calls are rejected. + */ + PJSUA_SIP_SIPREC_MANDATORY, + +} pjsua_sip_siprec_use; + + /** * This constants controls the use of 100rel extension. */ @@ -2343,6 +2369,15 @@ typedef struct pjsua_config */ pjsua_sip_timer_use use_timer; + /** + * Specify the usage of SIPREC sessions. See the + * #pjsua_sip_siprec_use for possible values. Note that this setting can be + * further customized in account configuration (#pjsua_acc_config). + * + * Default: PJSUA_SIP_SIPREC_INACTIVE + */ + pjsua_sip_siprec_use use_siprec; + /** * Handle unsolicited NOTIFY requests containing message waiting * indication (MWI) info. Unsolicited MWI is incoming NOTIFY requests @@ -4065,6 +4100,14 @@ typedef struct pjsua_acc_config */ pjsua_sip_timer_use use_timer; + /** + * Specify the usage of SIPREC sessions. See the + * #pjsua_sip_siprec_use for possible values. + * + * Default: PJSUA_SIP_SIPREC_INACTIVE + */ + pjsua_sip_siprec_use use_siprec; + /** * Specify Session Timer settings, see #pjsip_timer_setting. */ diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index 4750eb7ffc..c370cb46f4 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -241,6 +241,7 @@ struct pjsua_call unsigned hangup_code; /**< Hangup code. */ pj_str_t hangup_reason; /**< Hangup reason. */ pjsua_msg_data *hangup_msg_data;/**< Hangup message data. */ + pj_str_t siprec_metadata;/** siprec metadata in body */ }; diff --git a/pjsip/include/pjsua2/account.hpp b/pjsip/include/pjsua2/account.hpp index ad7d4472d4..c7eb021703 100644 --- a/pjsip/include/pjsua2/account.hpp +++ b/pjsip/include/pjsua2/account.hpp @@ -344,6 +344,14 @@ struct AccountCallConfig : public PersistentObject */ pjsua_sip_timer_use timerUse; + /** + * Specify the usage of SIPREC INVITE request. See the + * pjsua_sip_siprec_use for possible values. + * + * Default: PJSUA_SIP_SIPREC_INACTIVE + */ + pjsua_sip_siprec_use siprecUse; + /** * Specify minimum Session Timer expiration period, in seconds. * Must not be lower than 90. Default is 90. @@ -363,6 +371,7 @@ struct AccountCallConfig : public PersistentObject AccountCallConfig() : holdType(PJSUA_CALL_HOLD_TYPE_DEFAULT), prackUse(PJSUA_100REL_NOT_USED), timerUse(PJSUA_SIP_TIMER_OPTIONAL), + siprecUse(PJSUA_SIP_SIPREC_INACTIVE), timerMinSESec(90), timerSessExpiresSec(PJSIP_SESS_TIMER_DEF_SE) {} diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c index dafb7b8672..0ed94be75c 100644 --- a/pjsip/src/pjsip-ua/sip_inv.c +++ b/pjsip/src/pjsip-ua/sip_inv.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1278,7 +1279,9 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request3(pjsip_rx_data *rdata, *options |= PJSIP_INV_SUPPORT_ICE; if (*options & PJSIP_INV_REQUIRE_TRICKLE_ICE) *options |= PJSIP_INV_SUPPORT_TRICKLE_ICE; - + if (*options & PJSIP_INV_REQUIRE_SIPREC) + *options |= PJSIP_INV_SUPPORT_SIPREC; + if (rdata) { /* Get the message in rdata */ msg = rdata->msg_info.msg; @@ -1530,6 +1533,7 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request3(pjsip_rx_data *rdata, const pj_str_t STR_TIMER = { "timer", 5 }; const pj_str_t STR_ICE = { "ice", 3 }; const pj_str_t STR_TRICKLE_ICE = { "trickle-ice", 11 }; + const pj_str_t STR_SIPREC = { "siprec", 6 }; unsigned unsupp_cnt = 0; pj_str_t unsupp_tags[PJSIP_GENERIC_ARRAY_MAX_COUNT]; @@ -1539,6 +1543,11 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request3(pjsip_rx_data *rdata, { rem_option |= PJSIP_INV_REQUIRE_100REL; + } else if (!(*options & PJSIP_INV_SUPPORT_SIPREC) && + pj_stricmp(&req_hdr->values[i], &STR_SIPREC)==0) + { + unsupp_tags[unsupp_cnt++] = req_hdr->values[i]; + } else if ((*options & PJSIP_INV_SUPPORT_TIMER) && pj_stricmp(&req_hdr->values[i], &STR_TIMER)==0) { @@ -1546,7 +1555,7 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request3(pjsip_rx_data *rdata, } else if (pj_stricmp(&req_hdr->values[i], &STR_REPLACES)==0) { pj_bool_t supp; - + supp = pjsip_endpt_has_capability(endpt, PJSIP_H_SUPPORTED, NULL, &STR_REPLACES); if (!supp) diff --git a/pjsip/src/pjsip-ua/sip_siprec.c b/pjsip/src/pjsip-ua/sip_siprec.c new file mode 100644 index 0000000000..124b714bf1 --- /dev/null +++ b/pjsip/src/pjsip-ua/sip_siprec.c @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2024 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2024 Green and Silver Leaves. (https://github.com/BSVN) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define THIS_FILE "sip_siprec.c" + + +static const pj_str_t STR_SIPREC = {"siprec", 6}; +static const pj_str_t STR_LABEL = {"label", 5}; + + +/* Deinitialize siprec */ +static void pjsip_siprec_deinit_module(pjsip_endpoint *endpt) +{ + PJ_TODO(provide_initialized_flag_for_each_endpoint); + PJ_UNUSED_ARG(endpt); +} + + +/** + * Checks if the INVITE request is SIPREC. + */ +static pj_status_t pjsip_siprec_check_request(pjsip_rx_data *rdata) +{ + const pj_str_t str_require = {"Require", 7}; + const pj_str_t str_src = {"+sip.src", 8}; + pjsip_require_hdr *req_hdr; + pjsip_contact_hdr *contact_hdr; + + /* Find Require header */ + req_hdr = (pjsip_require_hdr*) + pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_require, NULL); + + if(!req_hdr || (pjsip_siprec_verify_require_hdr(req_hdr) == PJ_FALSE)){ + return PJ_FALSE; + } + + /* Find Contact header */ + contact_hdr = (pjsip_contact_hdr*) + pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); + + if(!contact_hdr || !contact_hdr->uri){ + return PJ_FALSE; + } + + /* Check "+sip.src" parameter exist in the Contact header */ + if(!pjsip_param_find(&contact_hdr->other_param, &str_src)){ + return PJ_FALSE; + } + return PJ_TRUE; +} + + +/** + * Initialize siprec support in PJSIP. + */ +PJ_DEF(pj_status_t) pjsip_siprec_init_module(pjsip_endpoint *endpt) +{ + pj_status_t status; + + PJ_ASSERT_RETURN(endpt, PJ_EINVAL); + + /* Register 'siprec' capability to endpoint */ + status = pjsip_endpt_add_capability(endpt, NULL, PJSIP_H_SUPPORTED, + NULL, 1, &STR_SIPREC); + + if (status != PJ_SUCCESS) + return status; + + /* Register deinit module to be executed when PJLIB shutdown */ + if (pjsip_endpt_atexit(endpt, &pjsip_siprec_deinit_module) != PJ_SUCCESS) + { + /* Failure to register this function may cause this module won't + * work properly when the stack is restarted (without quitting + * application). + */ + pj_assert(!"Failed to register Siprec deinit."); + PJ_LOG(1, (THIS_FILE, "Failed to register Siprec deinit.")); + } + + return PJ_SUCCESS; +} + + +/** + * Check if the value of Require header is equal to siprec. + */ +PJ_DEF(pj_status_t) pjsip_siprec_verify_require_hdr(pjsip_require_hdr *req_hdr) +{ + for (int i=0; icount; ++i) { + /* Check request has the siprec value in the Require header.*/ + if (pj_stricmp(&req_hdr->values[i], &STR_SIPREC)==0) + { + return PJ_TRUE; + } + } + return PJ_FALSE; +} + + +/** + * Verifies that the incoming request is a siprec request or not. + */ +PJ_DEF(pj_status_t) pjsip_siprec_verify_request(pjsip_rx_data *rdata, + pj_str_t *metadata, + pjmedia_sdp_session *sdp_offer, + unsigned *options, + pjsip_dialog *dlg, + pjsip_endpoint *endpt, + pjsip_tx_data **p_tdata) +{ + int code = 200; + pj_status_t status = PJ_SUCCESS; + const char *warn_text = NULL; + pjsip_hdr res_hdr_list; + pjsip_param *param; + + /* Init return arguments. */ + if (p_tdata) *p_tdata = NULL; + + /* Init response header list */ + pj_list_init(&res_hdr_list); + + /* Checks if The SIPREC request option is inactive */ + if (!(*options & PJSIP_INV_SUPPORT_SIPREC)){ + return PJ_SUCCESS; + } + + /* Checks if the INVITE request is SIPREC */ + if (pjsip_siprec_check_request(rdata) == PJ_FALSE){ + /* The SIPREC request option is mandatory */ + if (*options & PJSIP_INV_REQUIRE_SIPREC){ + code = PJSIP_SC_BAD_REQUEST; + warn_text = "The INVITE request must be SIPREC"; + goto on_return; + }else { + /* The SIPREC request option is optional */ + return PJ_SUCCESS; + } + } + + /* Checks if the body exists */ + if (!rdata->msg_info.msg->body) { + code = PJSIP_SC_BAD_REQUEST; + warn_text = "SIPREC INVITE must have a body"; + goto on_return; + } + + /* Currently, SIPREC INVITE requests without SDP are not supported. */ + if(!sdp_offer){ + code = PJSIP_SC_BAD_REQUEST; + warn_text = "SIPREC INVITE without SDP is not supported"; + goto on_return; + } + + /* Check that the media attribute label exist in the SDP */ + for (unsigned mi=0; mimedia_count; ++mi) { + if (!pjmedia_sdp_media_find_attr(sdp_offer->media[mi], + &STR_LABEL, NULL)){ + code = PJSIP_SC_BAD_REQUEST; + warn_text = "SDP must have label media attribute"; + goto on_return; + } + } + + status = pjsip_siprec_get_metadata(rdata->tp_info.pool, + rdata->msg_info.msg->body, + metadata); + + if(status != PJ_SUCCESS) { + code = PJSIP_SC_BAD_REQUEST; + warn_text = "SIPREC INVITE must have a 'rs-metadata' Content-Type"; + goto on_return; + } + + *options |= PJSIP_INV_REQUIRE_SIPREC; + + return status; + +on_return: + + /* Create response if necessary */ + if (code != 200) { + pjsip_tx_data *tdata; + const pjsip_hdr *h; + + if (dlg) { + status = pjsip_dlg_create_response(dlg, rdata, code, NULL, + &tdata); + } else { + status = pjsip_endpt_create_response(endpt, rdata, code, NULL, + &tdata); + } + + if (status != PJ_SUCCESS) + return status; + + /* Add response headers. */ + h = res_hdr_list.next; + while (h != &res_hdr_list) { + pjsip_hdr *cloned; + + cloned = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, h); + PJ_ASSERT_RETURN(cloned, PJ_ENOMEM); + + pjsip_msg_add_hdr(tdata->msg, cloned); + + h = h->next; + } + + /* Add warn text, if any */ + if (warn_text) { + pjsip_warning_hdr *warn_hdr; + pj_str_t warn_value = pj_str((char*)warn_text); + + warn_hdr=pjsip_warning_hdr_create(tdata->pool, 399, + pjsip_endpt_name(endpt), + &warn_value); + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)warn_hdr); + } + + *p_tdata = tdata; + + /* Can not return PJ_SUCCESS when response message is produced. + * Ref: PROTOS test ~#2490 + */ + if (status == PJ_SUCCESS) + status = PJSIP_ERRNO_FROM_SIP_STATUS(code); + + } + + return status; +} + + +/** + * Find siprec metadata from the message body + */ +PJ_DEF(pj_status_t) pjsip_siprec_get_metadata(pj_pool_t *pool, + pjsip_msg_body *body, + pj_str_t* metadata) +{ + pjsip_media_type application_metadata; + + pjsip_media_type_init2(&application_metadata, + "application", "rs-metadata"); + + pjsip_multipart_part *metadata_part; + metadata_part = pjsip_multipart_find_part(body, + &application_metadata, NULL); + + if(!metadata_part) + return PJ_ENOTFOUND; + + metadata->ptr = (char*)metadata_part->body->data; + metadata->slen = metadata_part->body->len; + + return PJ_SUCCESS; +} diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index bb6998d625..25ea8c1aad 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -1148,6 +1148,9 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id, acc->cfg.use_timer = cfg->use_timer; acc->cfg.timer_setting = cfg->timer_setting; + /* SIPREC */ + acc->cfg.use_siprec = cfg->use_siprec; + /* Transport */ if (acc->cfg.transport_id != cfg->transport_id) { pjsua_acc_set_transport(acc_id, cfg->transport_id); diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index fa3190b3dc..db020527e5 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -1350,6 +1350,9 @@ static pj_status_t verify_request(const pjsua_call *call, if (status == PJ_SUCCESS) { unsigned options = 0; + /* Add SIPREC support to prevent the "bad extension" error */ + options |= PJSIP_INV_SUPPORT_SIPREC; + /* Verify that we can handle the request. */ status = pjsip_inv_verify_request3(rdata, call->inv->pool_prov, &options, @@ -1808,6 +1811,45 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) /* Verify that we can handle the request. */ options |= PJSIP_INV_SUPPORT_100REL; options |= PJSIP_INV_SUPPORT_TIMER; + + if(pjsua_var.acc[acc_id].cfg.use_siprec != PJSUA_SIP_SIPREC_INACTIVE){ + options |= PJSIP_INV_SUPPORT_SIPREC; + if(pjsua_var.acc[acc_id].cfg.use_siprec == PJSUA_SIP_SIPREC_MANDATORY){ + options |= PJSIP_INV_REQUIRE_SIPREC; + } + } + + /* Check if the INVITE request is a siprec + * this function add PJSIP_INV_REQUIRE_SIPREC to options + * and returns the value PJ_SUCCESS + */ + status = pjsip_siprec_verify_request(rdata, &call->siprec_metadata, offer, + &options, NULL, pjsua_var.endpt, &response); + + if(status != PJ_SUCCESS){ + /* + * No we can't handle the incoming INVITE request. + */ + if (response) { + pjsip_response_addr res_addr; + ret_st_code = response->msg->line.status.code; + + pjsip_get_response_addr(response->pool, rdata, &res_addr); + status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, + response, NULL, NULL); + if (status != PJ_SUCCESS) pjsip_tx_data_dec_ref(response); + + } else { + /* Respond with 500 (Internal Server Error) */ + ret_st_code = PJSIP_SC_INTERNAL_SERVER_ERROR; + pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, ret_st_code, + NULL, NULL, NULL, NULL); + } + + goto on_return; + } + + if (pjsua_var.acc[acc_id].cfg.require_100rel == PJSUA_100REL_MANDATORY) options |= PJSIP_INV_REQUIRE_100REL; if (pjsua_var.acc[acc_id].cfg.ice_cfg.enable_ice) { diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 9f4cd232e3..569166eace 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -114,6 +114,7 @@ PJ_DEF(void) pjsua_config_default(pjsua_config *cfg) cfg->hangup_forked_call = PJ_TRUE; cfg->use_timer = PJSUA_SIP_TIMER_OPTIONAL; + cfg->use_siprec = PJSUA_SIP_SIPREC_INACTIVE; pjsip_timer_setting_default(&cfg->timer_setting); pjsua_srtp_opt_default(&cfg->srtp_opt); } @@ -335,6 +336,7 @@ PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg) cfg->require_100rel = pjsua_var.ua_cfg.require_100rel; cfg->use_timer = pjsua_var.ua_cfg.use_timer; cfg->timer_setting = pjsua_var.ua_cfg.timer_setting; + cfg->use_siprec = pjsua_var.ua_cfg.use_siprec; cfg->lock_codec = 1; cfg->ka_interval = 15; cfg->ka_data = pj_str("\r\n"); @@ -1161,6 +1163,10 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, status = pjsip_timer_init_module(pjsua_var.endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + /* Initialize siprec support */ + status = pjsip_siprec_init_module(pjsua_var.endpt); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + /* Initialize and register PJSUA application module. */ { const pjsip_module mod_initializer = diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index 59dd5300fc..07cae64fbe 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -3003,6 +3003,19 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, continue; } + /* Check if request supports PJSIP_INV_REQUIRE_SIPREC. If so Get label + * attribute in SDP offer and add label attribute to SDP answer + */ + if (call->inv && (call->inv->options & PJSIP_INV_REQUIRE_SIPREC)) { + pjmedia_sdp_attr *label_attr; + const pj_str_t STR_LABEL_ATTR = {"label", 5}; + + label_attr = pjmedia_sdp_media_find_attr( + rem_sdp->media[mi], &STR_LABEL_ATTR, NULL); + m->attr[m->attr_count++] = pjmedia_sdp_attr_create_label(pool, + &label_attr->value); + } + /* Add ssrc and cname attribute */ m->attr[m->attr_count++] = pjmedia_sdp_attr_create_ssrc(pool, call_med->ssrc, diff --git a/pjsip/src/pjsua2/account.cpp b/pjsip/src/pjsua2/account.cpp index 9807b14115..8968e1ee0a 100644 --- a/pjsip/src/pjsua2/account.cpp +++ b/pjsip/src/pjsua2/account.cpp @@ -320,6 +320,7 @@ void AccountCallConfig::readObject(const ContainerNode &node) NODE_READ_NUM_T ( this_node, pjsua_call_hold_type, holdType); NODE_READ_NUM_T ( this_node, pjsua_100rel_use, prackUse); NODE_READ_NUM_T ( this_node, pjsua_sip_timer_use, timerUse); + NODE_READ_NUM_T ( this_node, pjsua_sip_siprec_use, siprecUse); NODE_READ_UNSIGNED( this_node, timerMinSESec); NODE_READ_UNSIGNED( this_node, timerSessExpiresSec); } @@ -332,6 +333,7 @@ void AccountCallConfig::writeObject(ContainerNode &node) const NODE_WRITE_NUM_T ( this_node, pjsua_call_hold_type, holdType); NODE_WRITE_NUM_T ( this_node, pjsua_100rel_use, prackUse); NODE_WRITE_NUM_T ( this_node, pjsua_sip_timer_use, timerUse); + NODE_WRITE_NUM_T ( this_node, pjsua_sip_siprec_use, siprecUse); NODE_WRITE_UNSIGNED( this_node, timerMinSESec); NODE_WRITE_UNSIGNED( this_node, timerSessExpiresSec); } @@ -634,6 +636,7 @@ void AccountConfig::toPj(pjsua_acc_config &ret) const ret.call_hold_type = callConfig.holdType; ret.require_100rel = callConfig.prackUse; ret.use_timer = callConfig.timerUse; + ret.use_siprec = callConfig.siprecUse; ret.timer_setting.min_se = callConfig.timerMinSESec; ret.timer_setting.sess_expires = callConfig.timerSessExpiresSec; @@ -797,6 +800,7 @@ void AccountConfig::fromPj(const pjsua_acc_config &prm, callConfig.holdType = prm.call_hold_type; callConfig.prackUse = prm.require_100rel; callConfig.timerUse = prm.use_timer; + callConfig.siprecUse = prm.use_siprec; callConfig.timerMinSESec = prm.timer_setting.min_se; callConfig.timerSessExpiresSec = prm.timer_setting.sess_expires; From 0408536ef7d4082324edd1bfdec24336988fa539 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Wed, 8 Jan 2025 13:01:26 +0900 Subject: [PATCH 155/491] Refactor test apps to use unit-test framework (#4014) * Initial work on unittest framework, tested * Finished reorganizing pjlib-test to use unit-test framework * Fix big problem where performance improvement is not observed with the new framework (reason: because parallel flag is not set, doh!) * Tidying up global vars in pjlib-test. Add test options: stop on error, skip essential tests, list tests * Change the PJ_TEST_XX() signature to be more generic and does not force returning value * Modifications to some existing tests to use unit-test test macros * Updated VS projects with argparse.h and unittest.h * Fix warnings on Windows * Disable parallel unit-testing for ioqueue stress test on Windows because of errors when running on Windows virtual machine * Non-parallel test case will now run exclusively (previously it did not wait the previous test to complete) * Add pjlib-test/test_util.h for common utilities for parsing, configuring, and running unit test. This can be used by all test apps. pjlib-util-test has been ported to use this utilities * Dirty hack to fix error message being displayed when user selected essential test because it does not exist in features test (in pjlib-test) * Replace PJ_TEST_PARALLEL with PJ_TEST_EXCLUSIVE * Ported pjnath-test to use unit-testing framework, with limited speed-up from 45m originally to 15m using 10 worker threads * Large modifications in pjnath-test to speed-up test. It is fast now (4:30 minutes with 10 worker threads, from 45:42m originally) * Ported pjmedia-test to use unit-test * Porting of pjsip test to unit testing framework. Not much of speed improvements due to exclusive tests * Modifications in pj_argparse API (changed get() to get_bool() and add automatic error reporting) hopefully make it easier to use * Refactor tsx_uas_test() to allow parallel testing * Parallelize tsx_uac_test() * Further effort to parallize tests in pjsip-test. Discoverd major issue with unit-test logging (see unittest.md) * Showing PJLIB config is optional with cmd line option * Bug fixing failed pjsip tests when running in parallel mode * Finished paralleizing all pjsip tests except one * Continuing correcting errors * Add test shuffle feature * Modify CI workflows to use standard arguments: -w 3 --shuffle. These args are set in GitHub action variables * Updated due to change in argparse API signature (swap arg order) * Removed unittest.md (it was draft for the PR) * Fix the use of GitHub repository vars * Tidying up minor conflicting merge in VC projects and pjlib.h * Attempt to fix assertion failure when tdata is being accessed after reference counter goes to zero in regc_test * Fixing failure in transport_loop_test(), first in loop_resolve_error(), presumably because there are other loop transport around when the test is run. Solution are: 1) make the test exclusive for now, 2) fix the cleanup of loop transport in other tests. Then there is failure in transport_rt_test(), because it gets loop transport from other test that is being shutdown. Sneakily add pjsip_tpmgr_get_transport_count_by_type() that's useful for debugging. * Attempt to fix unresolved winmm.lib API (e.g. timeKillEvent, timeGetTime) called from BaseClasses * Split exclusive part of transport_loop_test * CI mods: split steps, envs, shorten job names * Fix failed tsx_basic_test * Restore previously shortened job names * Replace assertion with PJ_TEST_XX in resolver_test * Add --stdout-buf and --stderr-buf options in test apps to control stdout/stderr buffering * Fix missing sipp exe on Windows CI * Fix wrong CI args on Mac. Use -j for faster make * Replace pj_rand() with own rand in unittest because rand() yields different sequence on different platform even with the same srand, making it hard to reproduce test sequence * Fix swig make error on Linux and runall.py error reading log file on Windows * Attempt to fix test repeatability by 1) delete all calls to pj_srand() except in pj_test_suite_shuffle(), 2) unit test PRNG explicitly uses pj_uint32_t instead of int. Also disable windows python tests since it is unreliable * Fixed inexistant function * Relaxing the strictness of the test since sometimes it raises error * Set regc_test() exclusive because it crashes sometimes, probably concurrency issue * Modified thread counter to unsigned long (from pj_uint32_t) since the counter value is 2*1e9 and is overflow during diff calculation * Fixed port double destruction in mips_test() and include benchmark tests in pj/config_site_test.h * Use any port since sometimes test fails with address in use error * Fix conflicted return value in udp_ioqueue_test() and let it immediately exit on error so that we can see correlated error log * Restore sleep(0) in thread test since without it the test may occasionally fail on Linux * Various attempt to fix fluke error in resolve_test.c: 1) servers use random port numbers, 2) increase delay waiting for various DNS timers, 3) reset global vars to zero because test may be repeated for IPv6 * More relaxed packet count tests in resolver_test * Use any port instead of hardcoded one in udp ioqueue unregister_test since binding fails occasionally * Rollback previous changes in resolver_test that relaxed packet count test * Protect access to pool from worker thread with mutex in resolver_test * Faster resolver_test time by reducing timeout * Use high number port to make it less prone to bind error * Remove hardcoded port number, replace with bind to any * Fix SSL to continue decrypting data after renego completes * Fixed race condition when registering SIP module in transport test * Disable loading TLS cert in TURN sock test if SChannel is used * Add (missing!) pjnath-test in Windows CI * Fix syntax error in ci-win.yml and renamed lin->ubuntu in ci-linux * Temporary workaround for MacOS rwmutex deadlock issue * More jobs/tests in CI Mac, and changed names * Fix silly typo in config_site_test.h * Prettyfy CI job names, added more test steps * Fix CI: ffmpeg lib path, faster git clone * CI: simplify job names, add some audio checking * CI: Disable SDL check because vid renderer is not available on CI machine * Fixed missing ffmpeg shared lib when running pjmedia test * CI Mac: install gnutls * Lookup renderer in video codec test * CI MacOS: attempt to fix failed OpenSSL test * CI Mac: better test split to make duration more uniform across jobs * Replace deprecated egrep with grep -E --------- Co-authored-by: Nanang Izzuddin Co-authored-by: sauwming --- .github/workflows/ci-linux.yml | 191 +++-- .github/workflows/ci-mac.yml | 207 +++-- .github/workflows/ci-win.yml | 294 ++++++- Makefile | 16 +- pjlib-util/src/pjlib-util-test/main.c | 61 +- .../src/pjlib-util-test/resolver_test.c | 457 ++++++----- pjlib-util/src/pjlib-util-test/test.c | 73 +- pjlib-util/src/pjlib-util-test/test.h | 12 +- pjlib/build/pjlib.vcproj | 6 +- pjlib/build/pjlib.vcxproj.filters | 6 +- pjlib/include/pj/config_site_test.h | 5 + pjlib/src/pj/unittest.c | 34 +- pjlib/src/pjlib-test/activesock.c | 164 ++-- pjlib/src/pjlib-test/atomic.c | 42 +- pjlib/src/pjlib-test/fifobuf.c | 2 - pjlib/src/pjlib-test/file.c | 108 +-- pjlib/src/pjlib-test/hash_test.c | 70 +- pjlib/src/pjlib-test/ioq_perf.c | 119 ++- pjlib/src/pjlib-test/ioq_stress_test.c | 3 +- pjlib/src/pjlib-test/ioq_udp.c | 66 +- pjlib/src/pjlib-test/ioq_unreg.c | 2 +- pjlib/src/pjlib-test/main.c | 111 ++- pjlib/src/pjlib-test/main_rtems.c | 4 - pjlib/src/pjlib-test/main_win32.c | 7 +- pjlib/src/pjlib-test/rbtree.c | 41 +- pjlib/src/pjlib-test/sleep.c | 4 +- pjlib/src/pjlib-test/ssl_sock.c | 10 +- pjlib/src/pjlib-test/test.c | 363 ++++++--- pjlib/src/pjlib-test/test.h | 29 +- pjlib/src/pjlib-test/test_util.h | 317 ++++++++ pjlib/src/pjlib-test/thread.c | 23 +- pjlib/src/pjlib-test/timer.c | 6 - pjmedia/src/test/codec_vectors.c | 6 +- pjmedia/src/test/jbuf_test.c | 82 +- pjmedia/src/test/main.c | 57 +- pjmedia/src/test/mips_test.c | 8 +- pjmedia/src/test/test.c | 79 +- pjmedia/src/test/test.h | 14 +- pjmedia/src/test/vid_codec_test.c | 27 +- pjnath/src/pjnath-test/concur_test.c | 90 ++- pjnath/src/pjnath-test/ice_test.c | 224 +++--- pjnath/src/pjnath-test/main.c | 57 +- pjnath/src/pjnath-test/server.c | 53 +- pjnath/src/pjnath-test/server.h | 7 +- pjnath/src/pjnath-test/stun_sock_test.c | 43 +- pjnath/src/pjnath-test/test.c | 148 ++-- pjnath/src/pjnath-test/test.h | 43 +- pjnath/src/pjnath-test/turn_sock_test.c | 75 +- pjsip/include/pjsip/sip_transport.h | 13 + pjsip/src/pjsip/sip_resolve.c | 1 - pjsip/src/pjsip/sip_transport.c | 16 +- pjsip/src/test/dns_test.c | 12 + pjsip/src/test/inv_offer_answer_test.c | 58 +- pjsip/src/test/main.c | 119 ++- pjsip/src/test/regc_test.c | 26 +- pjsip/src/test/test.c | 335 +++----- pjsip/src/test/test.h | 49 +- pjsip/src/test/transport_loop_test.c | 286 +++++-- pjsip/src/test/transport_tcp_test.c | 23 +- pjsip/src/test/transport_test.c | 501 +++++++----- pjsip/src/test/transport_udp_test.c | 94 ++- pjsip/src/test/tsx_basic_test.c | 78 +- pjsip/src/test/tsx_bench.c | 73 +- pjsip/src/test/tsx_uac_test.c | 714 +++++++++------- pjsip/src/test/tsx_uas_test.c | 759 +++++++++++------- pjsip/src/test/txdata_test.c | 4 +- tests/pjsua/runall.py | 2 +- third_party/BaseClasses/renbase.cpp | 4 + 68 files changed, 4366 insertions(+), 2667 deletions(-) create mode 100644 pjlib/src/pjlib-test/test_util.h diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index faaca2cb59..7a5293a7fa 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -1,25 +1,34 @@ -name: CI Linux +name: CI Ubuntu on: push: branches: - 'master' pull_request: types: [opened, synchronize, reopened] +env: + CI_ARGS: ${{ vars.CI_UBUNTU_ARGS }} + CI_MODE: ${{ vars.CI_MODE }} + MAKE_FAST: make -j 3 jobs: - build-ubuntu-default: - # checking pure lib source distribution with plain configure & make + default-build: + # checking pure lib source distribution with plain configure & make runs-on: ubuntu-latest + name: Default / build only steps: - uses: actions/checkout@v2 - name: configure run: ./configure - name: make - run: make + run: $MAKE_FAST + - name: get SSL info + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL + - name: verify oepnssl is used + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep -E 'PJ_SSL_SOCK_IMP\s+:\s+1' - ubuntu-default-full-bundle-1: - # full bundle: enable all codecs + AEC + DTLS - # full bundle 1: running pjlib, pjlib-util, pjmedia, and pjsua tests + default-full-bundle-1: + # full bundle: enable all codecs + AEC + DTLS runs-on: ubuntu-latest + name: Default / pjmedia,pjsua steps: - uses: actions/checkout@v2 - name: install dependencies @@ -29,19 +38,33 @@ jobs: - name: configure run: CFLAGS="-g -fPIC" CXXFLAGS="-g -fPIC" LDFLAGS="-rdynamic" ./configure - name: make - run: make + run: $MAKE_FAST - name: swig bindings run: cd pjsip-apps/src/swig && make - name: set up Python 3.10 for pjsua test uses: actions/setup-python@v2 with: python-version: '3.10' - - name: unit tests - run: make pjlib-test-ci pjlib-util-test pjmedia-test pjsua-test + - name: capture pjsua capabilities + run: | + cat << EOF | pjsip-apps/bin/pjsua-`make infotarget` --log-level 3 > pjsua-caps + Cp + xx + vid dev list + vid codec list + q + EOF + cat pjsua-caps + - name: ensure AMR codec is installed + run: cat pjsua-caps | grep -E 'AMR/8000' + - name: pjmedia-test + run: make pjmedia-test + - name: pjsua-test + run: make pjsua-test - ubuntu-default-full-bundle-2: - # full bundle 2: running pjnath test + default-full-bundle-2: runs-on: ubuntu-latest + name: Default / pjlib,util,pjnath steps: - uses: actions/checkout@v2 - name: install dependencies @@ -51,13 +74,17 @@ jobs: - name: configure run: CFLAGS="-g" LDFLAGS="-rdynamic" ./configure - name: make - run: make - - name: unit tests + run: $MAKE_FAST + - name: pjlib-test + run: make pjlib-test + - name: pjlib-util-test + run: make pjlib-util-test + - name: pjnath-test run: make pjnath-test - ubuntu-default-full-bundle-3: - # full bundle 3: running pjsip test + default-full-bundle-3: runs-on: ubuntu-latest + name: Default / pjsip steps: - uses: actions/checkout@v2 - name: install dependencies @@ -67,13 +94,13 @@ jobs: - name: configure run: CFLAGS="-g" LDFLAGS="-rdynamic" ./configure - name: make - run: make - - name: unit tests + run: $MAKE_FAST + - name: pjsip-test run: make pjsip-test - build-ubuntu-no-tls: - # no TLS + no-tls: runs-on: ubuntu-latest + name: No SSL / pjlib,pjsip steps: - uses: actions/checkout@v2 - name: install dependencies @@ -81,16 +108,20 @@ jobs: - name: configure run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --disable-ssl - name: make - run: make + run: $MAKE_FAST - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: pjlib-test + run: make pjlib-test + - name: pjsip-test + run: make pjsip-test # build-ubuntu-openssl # TLS: with OpenSSL (same as build-ubuntu-default) - build-ubuntu-gnu-tls: - # TLS: with GnuTLS + gnu-tls: runs-on: ubuntu-latest + name: GnuTLS / pjlib,pjnath,pjsip steps: - uses: actions/checkout@v2 - name: install dependencies @@ -98,95 +129,149 @@ jobs: - name: configure run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --with-gnutls=/usr/ - name: make - run: make + run: $MAKE_FAST - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: get SSL info + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL + - name: verify gnu tls is used + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep -E 'PJ_SSL_SOCK_IMP\s+:\s+2' + - name: pjlib-test + run: make pjlib-test + - name: pjnath-test + run: make pjnath-test + - name: pjsip-test + run: make pjsip-test - ubuntu-video-openh264-1: - # video: video enabled with vpx and openh264 - # video 1: running pjlib, pjlib-util, pjmedia, and pjsua tests + vid-openh264-1: runs-on: ubuntu-latest + name: OpenH264+VPX / pjmedia,pjsua steps: - uses: actions/checkout@v2 - name: install dependencies - run: sudo apt-get install -y swig nasm sip-tester libvpx-dev libopencore-amrnb-dev + run: sudo apt-get install -y swig nasm sip-tester libvpx-dev libopencore-amrnb-dev libsdl2-dev - name: get openh264 - run: git clone --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git + run: git clone --depth 1 --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git - name: build openh264 - run: cd openh264 && make && sudo make install && sudo ldconfig + run: cd openh264 && $MAKE_FAST && sudo make install && sudo ldconfig - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h && echo "#define PJMEDIA_HAS_VIDEO 1" >> config_site.h - name: configure run: CFLAGS="-g -fPIC -DHAS_VID_CODEC_TEST=0" CXXFLAGS="-g -fPIC" LDFLAGS="-rdynamic" ./configure - name: make - run: make + run: $MAKE_FAST - name: swig bindings run: cd pjsip-apps/src/swig && make - name: set up Python 3.10 for pjsua test uses: actions/setup-python@v4 with: python-version: '3.10' - - name: unit tests - run: make pjlib-test-ci pjlib-util-test pjmedia-test pjsua-test + - name: capture pjsua capabilities + run: | + cat << EOF | pjsip-apps/bin/pjsua-`make infotarget` --log-level 3 > pjsua-caps + Cp + xx + vid dev list + vid codec list + q + EOF + cat pjsua-caps + - name: ensure AMR codec is installed + run: cat pjsua-caps | grep -E 'AMR/8000' + - name: ensure H264 codec is installed + run: cat pjsua-caps | grep -E 'H264/' + - name: ensure VP8 codec is installed + run: cat pjsua-caps | grep -E 'VP8/' + # SDL error: no available vid dev + #- name: ensure SDL is installed + # run: cat pjsua-caps | grep -E '\[SDL\]\[render\]' + - name: pjmedia-test + run: make pjmedia-test + - name: pjsua-test + run: make pjsua-test - ubuntu-video-openh264-2: - # video 2: running pjnath test + vid-openh264-2: runs-on: ubuntu-latest + name: OpenH264+VPX / pjlib,util,pjnath steps: - uses: actions/checkout@v2 - name: install dependencies - run: sudo apt-get install -y nasm libvpx-dev libopencore-amrnb-dev + run: sudo apt-get install -y nasm libvpx-dev libopencore-amrnb-dev libsdl2-dev - name: get openh264 - run: git clone --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git + run: git clone --depth 1 --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git - name: build openh264 - run: cd openh264 && make && sudo make install && sudo ldconfig + run: cd openh264 && $MAKE_FAST && sudo make install && sudo ldconfig - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h && echo "#define PJMEDIA_HAS_VIDEO 1" >> config_site.h - name: configure run: CFLAGS="-g" LDFLAGS="-rdynamic" ./configure - name: make - run: make - - name: unit tests + run: $MAKE_FAST + - name: pjlib-test + run: make pjlib-test + - name: pjlib-util-test + run: make pjlib-util-test + - name: pjnath-test run: make pjnath-test - ubuntu-video-openh264-3: - # video: 3: running pjsip test + vid-openh264-3: runs-on: ubuntu-latest + name: OpenH264+VPX / pjsip steps: - uses: actions/checkout@v2 - name: install dependencies - run: sudo apt-get install -y nasm libvpx-dev libopencore-amrnb-dev + run: sudo apt-get install -y nasm libvpx-dev libopencore-amrnb-dev libsdl2-dev - name: get openh264 - run: git clone --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git + run: git clone --depth 1 --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git - name: build openh264 - run: cd openh264 && make && sudo make install && sudo ldconfig + run: cd openh264 && $MAKE_FAST && sudo make install && sudo ldconfig - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h && echo "#define PJMEDIA_HAS_VIDEO 1" >> config_site.h - name: configure run: CFLAGS="-g" LDFLAGS="-rdynamic" ./configure - name: make - run: make - - name: unit tests + run: $MAKE_FAST + - name: pjsip-test run: make pjsip-test - build-ubuntu-video-ffmpeg: - # video enabled with vpx and ffmpeg and x264 + vid-ffmpeg: runs-on: ubuntu-latest + name: FFMPEG+x264 / pjmedia steps: - uses: actions/checkout@v2 - name: install dependencies - run: sudo apt-get install -y swig nasm libx264-dev libvpx-dev + run: sudo apt-get install -y swig nasm libx264-dev libvpx-dev libsdl2-dev - name: get ffmpeg - run: git clone --single-branch --branch release/4.2 https://github.com/FFmpeg/FFmpeg.git + run: git clone --depth 1 --single-branch --branch release/4.2 https://github.com/FFmpeg/FFmpeg.git - name: configure ffmpeg run: cd FFmpeg && ./configure --enable-shared --disable-static --enable-gpl --enable-libx264 - name: build ffmpeg - run: cd FFmpeg && make -j10 && sudo make install + run: cd FFmpeg && $MAKE_FAST && sudo make install - name: config site run: echo -e "#define PJMEDIA_HAS_VIDEO 1\n" > pjlib/include/pj/config_site.h - name: configure run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure - name: make - run: make + run: $MAKE_FAST - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: capture pjsua capabilities + run: | + export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH + cat << EOF | pjsip-apps/bin/pjsua-`make infotarget` --log-level 3 > pjsua-caps + Cp + xx + vid dev list + vid codec list + q + EOF + cat pjsua-caps + - name: ensure H264 codec is installed + run: cat pjsua-caps | grep -E 'H264/' + - name: ensure VP8 codec is installed + run: cat pjsua-caps | grep -E 'VP8/' + # SDL error: no available vid dev + #- name: ensure SDL is installed + # run: cat pjsua-caps | grep -E '\[SDL\]\[render\]' + - name: pjmedia-test + run: export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH && make pjmedia-test diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index d664932558..a550773842 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -1,67 +1,74 @@ -name: CI Mac +name: CI MacOS on: push: branches: - 'master' pull_request: types: [opened, synchronize, reopened] +env: + CI_ARGS: ${{ vars.CI_MAC_ARGS }} + CI_MODE: ${{ vars.CI_MODE }} + MAKE_FAST: make -j 2 jobs: - build-mac-default: - # checking pure lib source distribution with plain configure & make + default-build: + # checking pure lib source distribution with plain configure & make runs-on: macos-latest + name: Default / build only steps: - uses: actions/checkout@v2 - name: configure run: ./configure - name: make - run: make + run: $MAKE_FAST - mac-default-full-bundle-1: - # full bundle: enable all codecs + AEC + DTLS - # full bundle 1: running pjlib, pjlib-util, pjmedia, and pjsua tests + default-pjlib-util-pjmedia-pjnath: + # full bundle: enable all codecs + AEC + DTLS runs-on: macos-latest + name: Default / pjmedia,pjnath steps: - uses: actions/checkout@v2 - name: install dependencies - run: brew install openssl opencore-amr swig sipp + run: brew install openssl opencore-amr - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h - name: configure run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" CXXFLAGS="-g -fPIC" ./configure - name: make - run: make - - name: set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - name: swig bindings - run: cd pjsip-apps/src/swig && make + run: $MAKE_FAST - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - - name: unit tests - run: make pjlib-test-ci pjmedia-test pjlib-util-test pjsua-test + - name: pjmedia-test + run: make pjmedia-test + - name: pjnath-test + run: make pjnath-test - mac-default-full-bundle-2: - # full bundle 2: running pjnath test + default-pjsua-test: runs-on: macos-latest + name: Default / pjsua-test steps: - uses: actions/checkout@v2 - name: install dependencies - run: brew install openssl opencore-amr + run: brew install openssl opencore-amr swig sipp + - name: set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h - name: configure - run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb)" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" ./configure + run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" CXXFLAGS="-g -fPIC" ./configure - name: make - run: make + run: $MAKE_FAST - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - - name: unit tests - run: make pjnath-test + - name: swig bindings + run: cd pjsip-apps/src/swig && make + - name: pjsua-test + run: make pjsua-test - mac-default-full-bundle-3: - # full bundle 3: running pjsip test + default-pjsip-test: runs-on: macos-latest + name: Default / pjlib,util,pjsip-test steps: - uses: actions/checkout@v2 - name: install dependencies @@ -71,55 +78,123 @@ jobs: - name: configure run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb)" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" ./configure - name: make - run: make + run: $MAKE_FAST - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - - name: unit tests + - name: pjlib-test + run: make pjlib-test + - name: pjlib-util-test + run: make pjlib-util-test + - name: pjsip-test run: make pjsip-test - # build-ubuntu-no-tls: - # no TLS (same as build-mac-default) - - build-mac-openssl: - # TLS: with OpenSSL + openssl-1: runs-on: macos-latest + name: OpenSSL / pjlib,util,pjnath,pjmedia steps: - uses: actions/checkout@v2 - name: install dependencies run: brew install openssl swig - name: configure run: CFLAGS="$(pkg-config --cflags openssl) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib" CXXFLAGS="-fPIC" ./configure + - name: config site + run: cd pjlib/include/pj && cp config_site_test.h config_site.h - name: make - run: make + run: $MAKE_FAST - name: set up Python uses: actions/setup-python@v4 with: python-version: '3.10' - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: disable firewall + run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off + - name: get SSL info + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL + - name: verify openssl is used + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep -E 'PJ_SSL_SOCK_IMP\s+:\s+1' + - name: pjlib-test + run: make pjlib-test + - name: pjlib-util-test + run: make pjlib-util-test + - name: pjmedia-test + run: make pjmedia-test + - name: pjnath-test + run: make pjnath-test + + openssl-2: + runs-on: macos-latest + name: OpenSSL / pjsip + steps: + - uses: actions/checkout@v2 + - name: install dependencies + run: brew install openssl + - name: configure + run: CFLAGS="$(pkg-config --cflags openssl) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib" CXXFLAGS="-fPIC" ./configure + - name: config site + run: cd pjlib/include/pj && cp config_site_test.h config_site.h + - name: make + run: $MAKE_FAST + - name: disable firewall + run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off + - name: get SSL info + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL + - name: verify openssl is used + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep -E 'PJ_SSL_SOCK_IMP\s+:\s+1' + - name: pjsip-test + run: make pjsip-test - build-mac-gnu-tls: - # TLS: with GnuTLS + gnu-tls-1: runs-on: macos-latest + name: GnuTLS / pjlib,util,pjmedia,pjnath steps: - uses: actions/checkout@v2 - name: install dependencies - run: brew install swig + run: brew install gnutls swig - name: configure - run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --with-gnutls=/usr/local/ + run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --with-gnutls=`brew --prefix gnutls` - name: make - run: make + run: $MAKE_FAST - name: set up Python uses: actions/setup-python@v4 with: python-version: '3.10' - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: get SSL info + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL + - name: verify gnu tls is used + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep -E 'PJ_SSL_SOCK_IMP\s+:\s+2' + - name: pjlib-test + run: make pjlib-test + - name: pjlib-util-test + run: make pjlib-util-test + - name: pjmedia-test + run: make pjmedia-test + - name: pjnath-test + run: make pjnath-test + + gnu-tls-2: + runs-on: macos-latest + name: GnuTLS / pjsip-test + steps: + - uses: actions/checkout@v2 + - name: install dependencies + run: brew install gnutls swig + - name: configure + run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --with-gnutls=`brew --prefix gnutls` + - name: make + run: $MAKE_FAST + - name: get SSL info + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL + - name: verify gnu tls is used + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep -E 'PJ_SSL_SOCK_IMP\s+:\s+2' + - name: pjsip-test + run: make pjsip-test - mac-video-openh264-1: - # video: video enabled with vpx and openh264 - # video 1: running pjlib, pjlib-util, pjmedia, and pjsua tests + video-openh264-1: runs-on: macos-latest + name: Openh264+VPX / pjmedia,pjsua steps: - uses: actions/checkout@v2 - name: install dependencies @@ -129,7 +204,7 @@ jobs: - name: configure run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb) -DHAS_VID_CODEC_TEST=0 -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" CXXFLAGS="-g -fPIC" ./configure - name: make - run: make + run: $MAKE_FAST - name: set up Python uses: actions/setup-python@v4 with: @@ -138,12 +213,14 @@ jobs: run: cd pjsip-apps/src/swig && make - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - - name: unit tests - run: make pjlib-test-ci pjmedia-test pjlib-util-test pjsua-test + - name: pjmedia-test + run: make pjmedia-test + - name: pjsua-test + run: make pjsua-test - mac-video-openh264-2: - # video 2: running pjnath test + video-openh264-2: runs-on: macos-latest + name: Openh264+VPX / util,pjnath steps: - uses: actions/checkout@v2 - name: install dependencies @@ -153,15 +230,17 @@ jobs: - name: configure run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb)" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" ./configure - name: make - run: make + run: $MAKE_FAST - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - - name: unit tests + - name: pjlib-util-test + run: make pjlib-util-test + - name: pjnath-test run: make pjnath-test - mac-video-openh264-3: - # video 3: running pjsip test + video-openh264-3: runs-on: macos-latest + name: Openh264+VPX / pjlib,pjsip steps: - uses: actions/checkout@v2 - name: install dependencies @@ -171,41 +250,45 @@ jobs: - name: configure run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb)" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" ./configure - name: make - run: make + run: $MAKE_FAST - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - - name: unit tests + - name: pjlib-test + run: make pjlib-test + - name: pjsip-test run: make pjsip-test - build-mac-video-ffmpeg: - # video enabled with vpx and ffmpeg and x264 + video-ffmpeg: runs-on: macos-latest + name: FFMPEG+VPX+x264 / pjmedia steps: - uses: actions/checkout@v2 - name: install dependencies run: brew install openssl x264 libvpx nasm swig - name: get ffmpeg - run: git clone --single-branch --branch release/7.0 https://github.com/FFmpeg/FFmpeg.git + run: git clone --depth 1 --single-branch --branch release/7.0 https://github.com/FFmpeg/FFmpeg.git - name: configure ffmpeg run: cd FFmpeg && LDFLAGS="-Wl,-ld_classic" ./configure --enable-shared --disable-static --enable-gpl --enable-libx264 - name: build ffmpeg - run: cd FFmpeg && make -j10 && sudo make install + run: cd FFmpeg && $MAKE_FAST && sudo make install - name: config site run: echo -e "#define PJMEDIA_HAS_VIDEO 1\n" > pjlib/include/pj/config_site.h - name: configure run: CFLAGS="$(pkg-config --cflags openssl) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib" CXXFLAGS="-fPIC" ./configure - name: make - run: make + run: $MAKE_FAST - name: set up Python uses: actions/setup-python@v4 with: python-version: '3.10' - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: pjmedia-test + run: make pjmedia-test - build-mac-video-vid-toolbox: - # video enabled with vpx and video toolbox + video-vid-toolbox: runs-on: macos-latest + name: VPX+VidToolbox / pjmedia steps: - uses: actions/checkout@v2 - name: install dependencies @@ -215,10 +298,12 @@ jobs: - name: configure run: CFLAGS="$(pkg-config --cflags openssl) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib" CXXFLAGS="-fPIC" ./configure - name: make - run: make + run: $MAKE_FAST - name: set up Python uses: actions/setup-python@v4 with: python-version: '3.10' - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: pjmedia-test + run: make pjmedia-test diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index bff96d083c..6a76d386bc 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -6,8 +6,9 @@ on: pull_request: types: [opened, synchronize, reopened] jobs: - build-windows-default: + default: runs-on: windows-latest + name: Default / build only steps: - uses: actions/checkout@master - name: get swig @@ -40,8 +41,9 @@ jobs: msbuild swig_java_pjsua2.vcxproj /p:PlatformToolset=v143 /p:Configuration=Debug /p:Platform=win32 /p:UseEnv=true shell: cmd - windows-with-openssl-unit-test-1: + openssl-1: runs-on: windows-latest + name: OpenSSL / pjlib,util,pjmedia steps: - uses: actions/checkout@master - name: get openssl @@ -75,17 +77,31 @@ jobs: set LIB=%LIB%;%OPENSSL_DIR%\lib msbuild pjproject-vs14.sln /p:PlatformToolset=v143 /p:Configuration=Release /p:Platform=win32 /p:UseEnv=true shell: cmd - - name: unit tests + - name: pjlib-test run: | $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" - cd pjlib/bin; ./pjlib-test-i386-Win32-vc14-Release.exe --ci-mode - cd ../../pjlib-util/bin; ./pjlib-util-test-i386-Win32-vc14-Release.exe - cd ../../pjmedia/bin/; ./pjmedia-test-i386-Win32-vc14-Release.exe + cd pjlib/bin + ./pjlib-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} ${{ vars.CI_MODE }} + shell: powershell + - name: pjlib-util-test + run: | + $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt + $env:PATH+=";$env:OPENSSL_DIR\bin" + cd pjlib-util/bin + ./pjlib-util-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} + shell: powershell + - name: pjmedia-test + run: | + $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt + $env:PATH+=";$env:OPENSSL_DIR\bin" + cd pjmedia/bin + ./pjmedia-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} shell: powershell - windows-with-openssl-unit-test-2: + openssl-2: runs-on: windows-latest + name: OpenSSL / pjsip,pjsua steps: - uses: actions/checkout@master - name: get openssl @@ -103,6 +119,14 @@ jobs: dir "%OPENSSL_DIR%\include" dir "%OPENSSL_DIR%\lib" shell: cmd + - name: install SIPp + run: | + Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/sipp-3.2-win.zip" -OutFile ".\sipp.zip" + Expand-Archive -LiteralPath .\sipp.zip -DestinationPath . + cd sipp + Add-Content ..\sipp_dir.txt $pwd.Path + cd .. + shell: powershell - name: config site run: cd pjlib/include/pj; cp config_site_test.h config_site.h; Add-Content config_site.h "#define PJ_HAS_SSL_SOCK 1" @@ -132,16 +156,70 @@ jobs: uses: actions/setup-python@v4 with: python-version: '3.10' - - name: unit tests + - name: pjsip-test + run: | + $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt + $env:PATH+=";$env:OPENSSL_DIR\bin" + cd pjsip/bin + ./pjsip-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} + shell: powershell + - name: python pjsua tests run: | $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt + $env:SIPP_DIR = Get-Content .\sipp_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" - cd tests/pjsua; python runall.py - cd ../../pjsip/bin; ./pjsip-test-i386-Win32-vc14-Release.exe + $env:PATH+=";$env:SIPP_DIR" + cd tests/pjsua + echo python runall.py shell: powershell - build-windows-gnu-tls: + openssl-3: runs-on: windows-latest + name: OpenSSL / pjnath + steps: + - uses: actions/checkout@master + - name: get openssl + run: Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/openssl-1.1.1s-win.zip" -OutFile ".\openssl.zip" + shell: powershell + - name: expand openssl + run: | + Expand-Archive -LiteralPath .\openssl.zip -DestinationPath .; + cd openssl_build + Add-Content ..\openssl_dir.txt $pwd.Path + shell: powershell + - name: check openssl folder + run: | + set /P OPENSSL_DIR= /dev/null || true $(RM) $(addprefix $(DESTDIR)$(libdir)/,$(notdir $(APP_LIBXX_FILES))) rmdir $(DESTDIR)$(libdir) 2> /dev/null || true + +infotarget: + @echo $(TARGET_NAME) diff --git a/pjlib-util/src/pjlib-util-test/main.c b/pjlib-util/src/pjlib-util-test/main.c index a5413f162c..7c4ec7a049 100644 --- a/pjlib-util/src/pjlib-util-test/main.c +++ b/pjlib-util/src/pjlib-util-test/main.c @@ -18,6 +18,8 @@ */ #include "test.h" #include +#include + #if defined(PJ_SUNOS) && PJ_SUNOS!=0 @@ -34,19 +36,19 @@ static void init_signals() #elif (PJ_LINUX || PJ_DARWINOS) && defined(PJ_HAS_EXECINFO_H) && PJ_HAS_EXECINFO_H != 0 -#include +#include #include -#include -#include -#include +#include +#include +#include static void print_stack(int sig) { - void *array[16]; - size_t size; - - size = backtrace(array, 16); - fprintf(stderr, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, STDERR_FILENO); + void *array[16]; + size_t size; + + size = backtrace(array, 16); + fprintf(stderr, "Error: signal %d:\n", sig); + backtrace_symbols_fd(array, size, STDERR_FILENO); exit(1); } @@ -64,6 +66,22 @@ static void init_signals(void) #define boost() +static void usage() +{ + puts("Usage:"); + puts(" pjlib-util-test [OPTION] [test_to_run] [..]"); + puts(""); + puts("where OPTIONS:"); + puts(""); + puts(" -h, --help Show this help screen"); + + ut_usage(); + + puts(" -i Ask ENTER before quitting"); + puts(" -n Do not trap signals"); +} + + int main(int argc, char *argv[]) { int rc; @@ -72,22 +90,25 @@ int main(int argc, char *argv[]) boost(); - while (argc > 1) { - char *arg = argv[--argc]; - - if (*arg=='-' && *(arg+1)=='i') { - interractive = 1; - - } else if (*arg=='-' && *(arg+1)=='n') { - no_trap = 1; - } + if (pj_argparse_get_bool(&argc, argv, "-h") || + pj_argparse_get_bool(&argc, argv, "--help")) + { + usage(); + return 0; } + ut_app_init0(&test_app.ut_app); + + interractive = pj_argparse_get_bool(&argc, argv, "-i"); + no_trap = pj_argparse_get_bool(&argc, argv, "-n"); + if (ut_parse_args(&test_app.ut_app, &argc, argv)) + return 1; + if (!no_trap) { init_signals(); } - rc = test_main(); + rc = test_main(argc, argv); if (interractive) { char s[10]; diff --git a/pjlib-util/src/pjlib-util-test/resolver_test.c b/pjlib-util/src/pjlib-util-test/resolver_test.c index 40a6ad3260..2d23906ea1 100644 --- a/pjlib-util/src/pjlib-util-test/resolver_test.c +++ b/pjlib-util/src/pjlib-util-test/resolver_test.c @@ -21,7 +21,7 @@ #include "test.h" -#define THIS_FILE "srv_resolver_test.c" +#define THIS_FILE "resolver_test.c" //////////////////////////////////////////////////////////////////////////// /* @@ -61,6 +61,7 @@ static struct server_t } g_server[2]; static pj_pool_t *pool; +static pj_mutex_t *mutex; static pj_dns_resolver *resolver; static pj_bool_t thread_quit; static pj_timer_heap_t *timer_heap; @@ -81,6 +82,16 @@ struct label_tab } a[MAX_LABEL]; }; +static void lock() +{ + pj_mutex_lock(mutex); +} + +static void unlock() +{ + pj_mutex_unlock(mutex); +} + static void write16(pj_uint8_t *p, pj_uint16_t val) { p[0] = (pj_uint8_t)(val >> 8); @@ -170,7 +181,7 @@ static int print_rr(pj_uint8_t *pkt, int size, pj_uint8_t *pos, if (size < 8) return -1; - pj_assert(rr->dnsclass == 1); + PJ_TEST_EQ(rr->dnsclass, 1, NULL, return -1); write16(p+0, (pj_uint16_t)rr->type); /* type */ write16(p+2, (pj_uint16_t)rr->dnsclass); /* class */ @@ -369,14 +380,17 @@ static int server_thread(void *p) } PJ_LOG(5,(THIS_FILE, "Server %ld processing packet", srv - &g_server[0])); - srv->pkt_count++; + lock(); rc = pj_dns_parse_packet(pool, pkt, (unsigned)pkt_len, &req); + unlock(); if (rc != PJ_SUCCESS) { app_perror("server error parsing packet", rc); continue; } + srv->pkt_count++; + /* Verify packet */ if (req->hdr.qdcount != 1) { PJ_LOG(5,(THIS_FILE, "server receive multiple queries in a packet")); @@ -418,7 +432,7 @@ static int poll_worker_thread(void *p) PJ_UNUSED_ARG(p); while (!thread_quit) { - pj_time_val delay = {0, 100}; + pj_time_val delay = {0, 10}; pj_timer_heap_poll(timer_heap, NULL); pj_ioqueue_poll(ioqueue, &delay); } @@ -430,7 +444,6 @@ static void destroy(void); static int init(pj_bool_t use_ipv6) { - pj_status_t status; pj_str_t nameservers[2]; pj_uint16_t ports[2]; int i; @@ -442,60 +455,57 @@ static int init(pj_bool_t use_ipv6) nameservers[0] = pj_str("127.0.0.1"); nameservers[1] = pj_str("127.0.0.1"); } - ports[0] = 5553; - ports[1] = 5554; - - g_server[0].port = ports[0]; - g_server[1].port = ports[1]; pool = pj_pool_create(mem, NULL, 2000, 2000, NULL); - status = pj_sem_create(pool, NULL, 0, 2, &sem); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_mutex_create_simple(pool, "resolver_test", &mutex), + NULL, return -3); + PJ_TEST_SUCCESS(pj_sem_create(pool, NULL, 0, 2, &sem), NULL, return -5); thread_quit = PJ_FALSE; for (i=0; i<2; ++i) { pj_sockaddr addr; + int namelen; - status = pj_sock_socket((use_ipv6? pj_AF_INET6() : pj_AF_INET()), - pj_SOCK_DGRAM(), 0, &g_server[i].sock); - if (status != PJ_SUCCESS) - return -10; + PJ_TEST_SUCCESS(pj_sock_socket( + (use_ipv6? pj_AF_INET6() : pj_AF_INET()), + pj_SOCK_DGRAM(), 0, &g_server[i].sock), + NULL, return -10); pj_sockaddr_init((use_ipv6? pj_AF_INET6() : pj_AF_INET()), - &addr, NULL, (pj_uint16_t)g_server[i].port); - - status = pj_sock_bind(g_server[i].sock, &addr, pj_sockaddr_get_len(&addr)); - if (status != PJ_SUCCESS) - return -20; - - status = pj_thread_create(pool, NULL, &server_thread, &g_server[i], - 0, 0, &g_server[i].thread); - if (status != PJ_SUCCESS) - return -30; + &addr, NULL, 0); + + PJ_TEST_SUCCESS(pj_sock_bind(g_server[i].sock, &addr, + pj_sockaddr_get_len(&addr)), + NULL, return -20); + + namelen = sizeof(addr); + PJ_TEST_SUCCESS(pj_sock_getsockname(g_server[i].sock, &addr, + &namelen), + NULL, return -25); + g_server[i].port = ports[i] = pj_sockaddr_get_port(&addr); + + PJ_TEST_SUCCESS(pj_thread_create(pool, NULL, &server_thread, + &g_server[i], + 0, 0, &g_server[i].thread), + NULL, return -30); } - status = pj_timer_heap_create(pool, 16, &timer_heap); - pj_assert(status == PJ_SUCCESS); - - status = pj_ioqueue_create(pool, 16, &ioqueue); - pj_assert(status == PJ_SUCCESS); - - status = pj_dns_resolver_create(mem, NULL, 0, timer_heap, ioqueue, &resolver); - if (status != PJ_SUCCESS) - return -40; + PJ_TEST_SUCCESS(pj_timer_heap_create(pool, 16, &timer_heap), NULL, return -31); + PJ_TEST_SUCCESS(pj_ioqueue_create(pool, 16, &ioqueue), NULL, return -32); + PJ_TEST_SUCCESS(pj_dns_resolver_create(mem, NULL, 0, timer_heap, ioqueue, &resolver), + NULL, return -40); pj_dns_resolver_get_settings(resolver, &set); - set.good_ns_ttl = 20; - set.bad_ns_ttl = 20; + set.good_ns_ttl = 10; + set.bad_ns_ttl = 10; pj_dns_resolver_set_settings(resolver, &set); - status = pj_dns_resolver_set_ns(resolver, 2, nameservers, ports); - pj_assert(status == PJ_SUCCESS); - - status = pj_thread_create(pool, NULL, &poll_worker_thread, NULL, 0, 0, &poll_thread); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_dns_resolver_set_ns(resolver, 2, nameservers, ports), + NULL, return -41); + PJ_TEST_SUCCESS(pj_thread_create(pool, NULL, &poll_worker_thread, NULL, 0, 0, &poll_thread), + NULL, return -42); return 0; } @@ -503,6 +513,9 @@ static int init(pj_bool_t use_ipv6) static void destroy(void) { + /* note: need to set global vars back to zero since test can be + * repeated for IPv6 + */ int i; thread_quit = PJ_TRUE; @@ -513,13 +526,24 @@ static void destroy(void) } pj_thread_join(poll_thread); + poll_thread = NULL; + thread_quit = PJ_FALSE; pj_dns_resolver_destroy(resolver, PJ_FALSE); + resolver = NULL; pj_ioqueue_destroy(ioqueue); + ioqueue = NULL; pj_timer_heap_destroy(timer_heap); + timer_heap = NULL; pj_sem_destroy(sem); + sem = NULL; + pj_mutex_destroy(mutex); + mutex = NULL; pj_pool_release(pool); + pool = NULL; + + pj_bzero(g_server, sizeof(g_server)); } @@ -529,7 +553,6 @@ static int a_parser_test(void) { pj_dns_parsed_packet pkt; pj_dns_a_record rec; - pj_status_t rc; PJ_LOG(3,(THIS_FILE, " DNS A record parser tests")); @@ -570,12 +593,11 @@ static int a_parser_test(void) pkt.ans[2].rdata.a.ip_addr.s_addr = 0x0203; - rc = pj_dns_parse_a_response(&pkt, &rec); - pj_assert(rc == PJ_SUCCESS); - pj_assert(pj_strcmp2(&rec.name, "ahost")==0); - pj_assert(rec.alias.slen == 0); - pj_assert(rec.addr_count == 1); - pj_assert(rec.addr[0].s_addr == 0x01020304); + PJ_TEST_SUCCESS(pj_dns_parse_a_response(&pkt, &rec), NULL, return -100) + PJ_TEST_EQ(pj_strcmp2(&rec.name, "ahost"), 0, NULL, return -110); + PJ_TEST_EQ(rec.alias.slen, 0, NULL, return -112); + PJ_TEST_EQ(rec.addr_count, 1, NULL, return -114); + PJ_TEST_EQ(rec.addr[0].s_addr, 0x01020304, NULL, return -116); /* Answer with the target corresponds to a CNAME entry, but not * as the first record, and with additions of some CNAME and A @@ -617,12 +639,11 @@ static int a_parser_test(void) pkt.ans[3].ttl = 1; pkt.ans[3].rdata.a.ip_addr.s_addr = 0x03030303; - rc = pj_dns_parse_a_response(&pkt, &rec); - pj_assert(rc == PJ_SUCCESS); - pj_assert(pj_strcmp2(&rec.name, "ahost")==0); - pj_assert(pj_strcmp2(&rec.alias, "ahostalias")==0); - pj_assert(rec.addr_count == 1); - pj_assert(rec.addr[0].s_addr == 0x02020202); + PJ_TEST_SUCCESS(pj_dns_parse_a_response(&pkt, &rec), NULL, return -120); + PJ_TEST_EQ(pj_strcmp2(&rec.name, "ahost"), 0, NULL, return -122); + PJ_TEST_EQ(pj_strcmp2(&rec.alias, "ahostalias"), 0, NULL, return -124); + PJ_TEST_EQ(rec.addr_count, 1, NULL, return -126); + PJ_TEST_EQ(rec.addr[0].s_addr, 0x02020202, NULL, return -128); /* * No query section. @@ -631,8 +652,8 @@ static int a_parser_test(void) pkt.hdr.qdcount = 0; pkt.hdr.anscount = 0; - rc = pj_dns_parse_a_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSINANSWER); + PJ_TEST_EQ(pj_dns_parse_a_response(&pkt, &rec), PJLIB_UTIL_EDNSINANSWER, + NULL, return -130); /* * No answer section. @@ -645,8 +666,8 @@ static int a_parser_test(void) pkt.q[0].name = pj_str("ahost"); pkt.hdr.anscount = 0; - rc = pj_dns_parse_a_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); + PJ_TEST_EQ(pj_dns_parse_a_response(&pkt, &rec), PJLIB_UTIL_EDNSNOANSWERREC, + NULL, return -140); /* * Answer doesn't match query. @@ -666,8 +687,8 @@ static int a_parser_test(void) pkt.ans[0].ttl = 1; pkt.ans[0].rdata.a.ip_addr.s_addr = 0x02020202; - rc = pj_dns_parse_a_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); + PJ_TEST_EQ(pj_dns_parse_a_response(&pkt, &rec), PJLIB_UTIL_EDNSNOANSWERREC, + NULL, return -150); /* @@ -688,8 +709,8 @@ static int a_parser_test(void) pkt.ans[0].ttl = 1; pkt.ans[0].rdata.cname.name = pj_str("ahostalias"); - rc = pj_dns_parse_a_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); + PJ_TEST_EQ(pj_dns_parse_a_response(&pkt, &rec), PJLIB_UTIL_EDNSNOANSWERREC, + NULL, return -160); /* @@ -717,9 +738,8 @@ static int a_parser_test(void) pkt.ans[1].ttl = 1; pkt.ans[1].rdata.a.ip_addr.s_addr = 0x01020304; - rc = pj_dns_parse_a_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); - PJ_UNUSED_ARG(rc); + PJ_TEST_EQ(pj_dns_parse_a_response(&pkt, &rec), PJLIB_UTIL_EDNSNOANSWERREC, + NULL, return -170); return 0; } @@ -731,7 +751,6 @@ static int addr_parser_test(void) { pj_dns_parsed_packet pkt; pj_dns_addr_record rec; - pj_status_t rc; PJ_LOG(3,(THIS_FILE, " DNS A/AAAA record parser tests")); @@ -779,13 +798,14 @@ static int addr_parser_test(void) s6_addr32(pkt.ans[3].rdata.aaaa.ip_addr, 0) = 0x01020304; - rc = pj_dns_parse_addr_response(&pkt, &rec); - pj_assert(rc == PJ_SUCCESS); - pj_assert(pj_strcmp2(&rec.name, "ahost")==0); - pj_assert(rec.alias.slen == 0); - pj_assert(rec.addr_count == 2); - pj_assert(rec.addr[0].af==pj_AF_INET() && rec.addr[0].ip.v4.s_addr == 0x01020304); - pj_assert(rec.addr[1].af==pj_AF_INET6() && s6_addr32(rec.addr[1].ip.v6, 0) == 0x01020304); + PJ_TEST_SUCCESS(pj_dns_parse_addr_response(&pkt, &rec), NULL, return -200); + PJ_TEST_EQ(pj_strcmp2(&rec.name, "ahost"), 0, NULL, return -210); + PJ_TEST_EQ(rec.alias.slen, 0, NULL, return -212); + PJ_TEST_EQ(rec.addr_count, 2, NULL, return -214); + PJ_TEST_EQ(rec.addr[0].af, pj_AF_INET(), NULL, return -220); + PJ_TEST_EQ(rec.addr[0].ip.v4.s_addr, 0x01020304, NULL, return -222); + PJ_TEST_EQ(rec.addr[1].af, pj_AF_INET6(), NULL, return -230); + PJ_TEST_EQ(s6_addr32(rec.addr[1].ip.v6, 0), 0x01020304, NULL, return -232); /* Answer with the target corresponds to a CNAME entry, but not * as the first record, and with additions of some CNAME and A @@ -827,12 +847,11 @@ static int addr_parser_test(void) pkt.ans[3].ttl = 1; pkt.ans[3].rdata.a.ip_addr.s_addr = 0x03030303; - rc = pj_dns_parse_addr_response(&pkt, &rec); - pj_assert(rc == PJ_SUCCESS); - pj_assert(pj_strcmp2(&rec.name, "ahost")==0); - pj_assert(pj_strcmp2(&rec.alias, "ahostalias")==0); - pj_assert(rec.addr_count == 1); - pj_assert(rec.addr[0].ip.v4.s_addr == 0x02020202); + PJ_TEST_SUCCESS(pj_dns_parse_addr_response(&pkt, &rec), NULL, return -240); + PJ_TEST_EQ(pj_strcmp2(&rec.name, "ahost"), 0, NULL, return -242); + PJ_TEST_EQ(pj_strcmp2(&rec.alias, "ahostalias"), 0, NULL, return -244); + PJ_TEST_EQ(rec.addr_count, 1, NULL, return -246); + PJ_TEST_EQ(rec.addr[0].ip.v4.s_addr, 0x02020202, NULL, return -248); /* * No query section. @@ -841,8 +860,8 @@ static int addr_parser_test(void) pkt.hdr.qdcount = 0; pkt.hdr.anscount = 0; - rc = pj_dns_parse_addr_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSINANSWER); + PJ_TEST_EQ(pj_dns_parse_addr_response(&pkt, &rec), + PJLIB_UTIL_EDNSINANSWER, NULL, return -245); /* * No answer section. @@ -855,8 +874,8 @@ static int addr_parser_test(void) pkt.q[0].name = pj_str("ahost"); pkt.hdr.anscount = 0; - rc = pj_dns_parse_addr_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); + PJ_TEST_EQ(pj_dns_parse_addr_response(&pkt, &rec), + PJLIB_UTIL_EDNSNOANSWERREC, NULL, return -250); /* * Answer doesn't match query. @@ -876,8 +895,8 @@ static int addr_parser_test(void) pkt.ans[0].ttl = 1; pkt.ans[0].rdata.a.ip_addr.s_addr = 0x02020202; - rc = pj_dns_parse_addr_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); + PJ_TEST_EQ(pj_dns_parse_addr_response(&pkt, &rec), + PJLIB_UTIL_EDNSNOANSWERREC, NULL, return -260); /* @@ -898,8 +917,8 @@ static int addr_parser_test(void) pkt.ans[0].ttl = 1; pkt.ans[0].rdata.cname.name = pj_str("ahostalias"); - rc = pj_dns_parse_addr_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); + PJ_TEST_EQ(pj_dns_parse_addr_response(&pkt, &rec), + PJLIB_UTIL_EDNSNOANSWERREC, NULL, return -270); /* @@ -927,9 +946,8 @@ static int addr_parser_test(void) pkt.ans[1].ttl = 1; pkt.ans[1].rdata.a.ip_addr.s_addr = 0x01020304; - rc = pj_dns_parse_addr_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); - PJ_UNUSED_ARG(rc); + PJ_TEST_EQ(pj_dns_parse_addr_response(&pkt, &rec), + PJLIB_UTIL_EDNSNOANSWERREC, NULL, return -280); return 0; } @@ -960,7 +978,6 @@ static int simple_test(void) { pj_str_t name = pj_str("helloworld"); pj_dns_parsed_packet *r; - pj_status_t status; PJ_LOG(3,(THIS_FILE, " simple successful test")); @@ -995,18 +1012,18 @@ static int simple_test(void) r->ans[0].name = name; r->ans[0].rdata.a.ip_addr.s_addr = IP_ADDR0; - status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, - &dns_callback, NULL, NULL); - if (status != PJ_SUCCESS) - return -1000; + PJ_TEST_SUCCESS(pj_dns_resolver_start_query( + resolver, &name, PJ_DNS_TYPE_A, 0, + &dns_callback, NULL, NULL), + NULL, return -300); pj_sem_wait(sem); - pj_thread_sleep(1000); + pj_thread_sleep(set.qretr_delay * 1.2); /* Both servers must get packet */ - pj_assert(g_server[0].pkt_count == 1); - pj_assert(g_server[1].pkt_count == 1); + PJ_TEST_EQ(g_server[0].pkt_count, 1, NULL, return -310); + PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -320); return 0; } @@ -1024,8 +1041,8 @@ static void dns_callback_1b(void *user_data, pj_sem_post(sem); - PJ_ASSERT_ON_FAIL(status==PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_RCODE_NXDOMAIN), - return); + PJ_TEST_EQ(status, PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_RCODE_NXDOMAIN), + NULL, return); } @@ -1035,7 +1052,7 @@ static void dns_callback_1b(void *user_data, static int dns_test(void) { pj_str_t name = pj_str("name00"); - pj_status_t status; + enum { D = 2 }; PJ_LOG(3,(THIS_FILE, " simple error response test")); @@ -1045,10 +1062,10 @@ static int dns_test(void) g_server[0].action = PJ_DNS_RCODE_NXDOMAIN; g_server[1].action = PJ_DNS_RCODE_NXDOMAIN; - status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, - &dns_callback_1b, NULL, NULL); - if (status != PJ_SUCCESS) - return -1000; + PJ_TEST_SUCCESS(pj_dns_resolver_start_query( + resolver, &name, PJ_DNS_TYPE_A, 0, + &dns_callback_1b, NULL, NULL), + NULL, return -400); pj_sem_wait(sem); pj_thread_sleep(1000); @@ -1056,12 +1073,13 @@ static int dns_test(void) /* Now only one of the servers should get packet, since both servers are * in STATE_ACTIVE state */ - pj_assert(g_server[0].pkt_count + g_server[1].pkt_count == 1); + PJ_TEST_EQ(g_server[0].pkt_count + g_server[1].pkt_count, 1, + NULL, return -410); /* Wait to allow active period to complete and get into probing state */ PJ_LOG(3,(THIS_FILE, " waiting for active NS to expire (%d sec)", set.good_ns_ttl)); - pj_thread_sleep(set.good_ns_ttl * 1000); + pj_thread_sleep((set.good_ns_ttl+D) * 1000); /* * Fail-over test @@ -1074,15 +1092,16 @@ static int dns_test(void) g_server[1].pkt_count = 0; name = pj_str("name01"); - status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, - &dns_callback_1b, NULL, NULL); - if (status != PJ_SUCCESS) - return -1000; + PJ_TEST_SUCCESS(pj_dns_resolver_start_query( + resolver, &name, PJ_DNS_TYPE_A, 0, + &dns_callback_1b, NULL, NULL), + NULL, return -420); pj_sem_wait(sem); /* Both servers must get packet as both are in probing state */ - pj_assert(g_server[0].pkt_count >= 1 && g_server[1].pkt_count == 1); + PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -430); + PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -435); /* * Check that both servers still receive requests, since they are @@ -1096,22 +1115,23 @@ static int dns_test(void) g_server[1].pkt_count = 0; name = pj_str("name02"); - status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, - &dns_callback_1b, NULL, NULL); - if (status != PJ_SUCCESS) - return -1000; + PJ_TEST_SUCCESS(pj_dns_resolver_start_query( + resolver, &name, PJ_DNS_TYPE_A, 0, + &dns_callback_1b, NULL, NULL), + NULL, return -440); pj_sem_wait(sem); pj_thread_sleep(1000); /* Both servers must get packet as both are in probing & active state */ - pj_assert(g_server[0].pkt_count >= 1 && g_server[1].pkt_count == 1); + PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -450); + PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -454); /* Wait to allow probing period to complete, server 0 will be in bad state */ PJ_LOG(3,(THIS_FILE, " waiting for probing state to end (%d sec)", set.qretr_delay * (set.qretr_count+2) / 1000)); - pj_thread_sleep(set.qretr_delay * (set.qretr_count + 2)); + pj_thread_sleep(1000 + set.qretr_delay * (set.qretr_count + 2)); /* @@ -1125,22 +1145,22 @@ static int dns_test(void) g_server[1].pkt_count = 0; name = pj_str("name03"); - status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, - &dns_callback_1b, NULL, NULL); - if (status != PJ_SUCCESS) - return -1000; + PJ_TEST_SUCCESS(pj_dns_resolver_start_query( + resolver, &name, PJ_DNS_TYPE_A, 0, + &dns_callback_1b, NULL, NULL), + NULL, return -460); pj_sem_wait(sem); pj_thread_sleep(1000); /* Only server 1 get the request */ - pj_assert(g_server[0].pkt_count == 0); - pj_assert(g_server[1].pkt_count == 1); + PJ_TEST_EQ(g_server[0].pkt_count, 0, NULL, return -470); + PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -474); /* Wait to allow active & bad period to complete, both will be in probing state */ PJ_LOG(3,(THIS_FILE, " waiting for active NS to expire (%d sec)", set.good_ns_ttl)); - pj_thread_sleep(set.good_ns_ttl * 1000); + pj_thread_sleep((set.good_ns_ttl+D) * 1000); /* * Now fail server 1 to switch to server 0 @@ -1152,10 +1172,10 @@ static int dns_test(void) g_server[1].pkt_count = 0; name = pj_str("name04"); - status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, - &dns_callback_1b, NULL, NULL); - if (status != PJ_SUCCESS) - return -1000; + PJ_TEST_SUCCESS(pj_dns_resolver_start_query( + resolver, &name, PJ_DNS_TYPE_A, 0, + &dns_callback_1b, NULL, NULL), + NULL, return -480); pj_sem_wait(sem); @@ -1175,18 +1195,17 @@ static int dns_test(void) g_server[1].pkt_count = 0; name = pj_str("name05"); - status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, - &dns_callback_1b, NULL, NULL); - if (status != PJ_SUCCESS) - return -1000; + PJ_TEST_SUCCESS(pj_dns_resolver_start_query( + resolver, &name, PJ_DNS_TYPE_A, 0, + &dns_callback_1b, NULL, NULL), + NULL, return -484); pj_sem_wait(sem); pj_thread_sleep(1000); /* Only good NS should get request */ - pj_assert(g_server[0].pkt_count == 1); - pj_assert(g_server[1].pkt_count == 0); - + PJ_TEST_EQ(g_server[0].pkt_count, 1, NULL, return -486); + PJ_TEST_EQ(g_server[1].pkt_count, 0, NULL, return -488); return 0; } @@ -1203,6 +1222,7 @@ static void action1_1(const pj_dns_parsed_packet *pkt, pj_dns_parsed_packet *res; char *target = "sip.somedomain.com"; + lock(); res = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet); if (res->q == NULL) { @@ -1212,6 +1232,7 @@ static void action1_1(const pj_dns_parsed_packet *pkt, res->ans = (pj_dns_parsed_rr*) pj_pool_calloc(pool, 4, sizeof(pj_dns_parsed_rr)); } + unlock(); res->hdr.qdcount = 1; res->q[0].type = pkt->q[0].type; @@ -1371,7 +1392,6 @@ static void srv_cb_1d(void *user_data, static int srv_resolver_test(void) { - pj_status_t status; pj_str_t domain = pj_str("somedomain.com"); pj_str_t res_name = pj_str("_sip._udp."); @@ -1388,15 +1408,16 @@ static int srv_resolver_test(void) g_server[0].pkt_count = 0; g_server[1].pkt_count = 0; - status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, PJ_TRUE, - NULL, &srv_cb_1, NULL); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, 5061, pool, resolver, PJ_TRUE, + NULL, &srv_cb_1, NULL), + NULL, return -500); pj_sem_wait(sem); /* Because of previous tests, only NS 1 should get the request */ - pj_assert(g_server[0].pkt_count == 2); /* 2 because of SRV and A resolution */ - pj_assert(g_server[1].pkt_count == 0); + PJ_TEST_EQ(g_server[0].pkt_count, 2, NULL, return -510); /* 2 because of SRV and A resolution */ + PJ_TEST_EQ(g_server[1].pkt_count, 0, NULL, return -512); /* Wait until cache expires */ @@ -1415,10 +1436,11 @@ static int srv_resolver_test(void) g_server[0].pkt_count = 0; g_server[1].pkt_count = 0; - status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, - PJ_DNS_SRV_RESOLVE_AAAA, - NULL, &srv_cb_1c, NULL); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, 5061, pool, resolver, + PJ_DNS_SRV_RESOLVE_AAAA, + NULL, &srv_cb_1c, NULL), + NULL, return -520); pj_sem_wait(sem); pj_thread_sleep(1000); @@ -1434,10 +1456,11 @@ static int srv_resolver_test(void) g_server[0].pkt_count = 0; g_server[1].pkt_count = 0; - status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, - PJ_DNS_SRV_RESOLVE_AAAA_ONLY, - NULL, &srv_cb_1d, NULL); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, 5061, pool, resolver, + PJ_DNS_SRV_RESOLVE_AAAA_ONLY, + NULL, &srv_cb_1d, NULL), + NULL, return -530); pj_sem_wait(sem); pj_thread_sleep(1000); @@ -1448,21 +1471,23 @@ static int srv_resolver_test(void) g_server[0].pkt_count = 0; g_server[1].pkt_count = 0; - status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, PJ_TRUE, - NULL, &srv_cb_1, NULL); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, 5061, pool, resolver, PJ_TRUE, + NULL, &srv_cb_1, NULL), + NULL, return -540); - status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, PJ_TRUE, - NULL, &srv_cb_1, NULL); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, 5061, pool, resolver, PJ_TRUE, + NULL, &srv_cb_1, NULL), + NULL, return -550); pj_sem_wait(sem); pj_sem_wait(sem); /* Only server one should get a query */ - pj_assert(g_server[0].pkt_count == 2); /* 2 because of SRV and A resolution */ - pj_assert(g_server[1].pkt_count == 0); + PJ_TEST_EQ(g_server[0].pkt_count, 2, NULL, return -554); /* 2 because of SRV and A resolution */ + PJ_TEST_EQ(g_server[1].pkt_count, 0, NULL, return -556); /* Since TTL is one, subsequent queries should fail */ PJ_LOG(3,(THIS_FILE, " srv_resolve(): cache expires scenario")); @@ -1472,14 +1497,15 @@ static int srv_resolver_test(void) g_server[0].action = PJ_DNS_RCODE_NXDOMAIN; g_server[1].action = PJ_DNS_RCODE_NXDOMAIN; - status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, PJ_TRUE, - NULL, &srv_cb_1b, NULL); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, 5061, pool, resolver, PJ_TRUE, + NULL, &srv_cb_1b, NULL), + NULL, return -560); pj_sem_wait(sem); pj_thread_sleep(1000); - return status; + return 0; } @@ -1494,11 +1520,13 @@ static void action2_1(const pj_dns_parsed_packet *pkt, { pj_dns_parsed_packet *res; + lock(); res = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet); res->q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query); res->ans = (pj_dns_parsed_rr*) pj_pool_calloc(pool, 4, sizeof(pj_dns_parsed_rr)); + unlock(); res->hdr.qdcount = 1; res->q[0].type = pkt->q[0].type; @@ -1642,7 +1670,6 @@ static void srv_cb_2b(void *user_data, static int srv_resolver_fallback_test(void) { - pj_status_t status; pj_str_t domain = pj_str(TARGET); pj_str_t res_name = pj_str("_sip._udp."); int cb_err = 0; @@ -1655,43 +1682,31 @@ static int srv_resolver_fallback_test(void) g_server[1].action = ACTION_CB; g_server[1].action_cb = &action2_1; - status = pj_dns_srv_resolve(&domain, &res_name, PORT2, pool, resolver, PJ_TRUE, - &cb_err, &srv_cb_2, NULL); - if (status != PJ_SUCCESS) { - app_perror(" srv_resolve error", status); - return -10; - } + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, PORT2, pool, resolver, PJ_TRUE, + &cb_err, &srv_cb_2, NULL), + NULL, return -600); pj_sem_wait(sem); - if (cb_err != 0) { - PJ_LOG(3,("test", " srv_resolve cb error, code=%d", cb_err)); - return -20; - } + PJ_TEST_EQ(cb_err, 0, "srv_resolve cb error", return -605); /* Subsequent query should just get the response from the cache */ PJ_LOG(3,(THIS_FILE, " srv_resolve(): cache test")); g_server[0].pkt_count = 0; g_server[1].pkt_count = 0; - status = pj_dns_srv_resolve(&domain, &res_name, PORT2, pool, resolver, PJ_TRUE, - &cb_err, &srv_cb_2, NULL); - if (status != PJ_SUCCESS) { - app_perror(" srv_resolve error", status); - return -30; - } + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, PORT2, pool, resolver, PJ_TRUE, + &cb_err, &srv_cb_2, NULL), + NULL, return -610); pj_sem_wait(sem); - if (cb_err != 0) { - PJ_LOG(3,("test", " srv_resolve cb error, code=%d", cb_err)); - return -40; - } + PJ_TEST_EQ(cb_err, 0, "srv_resolve cb error", return -615); - if (g_server[0].pkt_count != 0 || g_server[1].pkt_count != 0) { - PJ_LOG(3,("test", " srv_resolve() not from cache")); - return -50; - } + PJ_TEST_EQ(g_server[0].pkt_count, 0, "must be from cache", return -620); + PJ_TEST_EQ(g_server[1].pkt_count, 0, "must be from cache", return -625); /* Clear cache */ pj_thread_sleep(1000); @@ -1704,20 +1719,15 @@ static int srv_resolver_fallback_test(void) g_server[1].action = ACTION_CB; g_server[1].action_cb = &action2_1; - status = pj_dns_srv_resolve(&domain, &res_name, PORT2, pool, resolver, - PJ_DNS_SRV_FALLBACK_A | PJ_DNS_SRV_FALLBACK_AAAA, - &cb_err, &srv_cb_2a, NULL); - if (status != PJ_SUCCESS) { - app_perror(" srv_resolve error", status); - return -60; - } + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, PORT2, pool, resolver, + PJ_DNS_SRV_FALLBACK_A | PJ_DNS_SRV_FALLBACK_AAAA, + &cb_err, &srv_cb_2a, NULL), + NULL, return -630); pj_sem_wait(sem); - if (cb_err != 0) { - PJ_LOG(3,("test", " srv_resolve cb error, code=%d", cb_err)); - return -70; - } + PJ_TEST_EQ(cb_err, 0, "srv_resolve cb error", return -635); /* Clear cache */ pj_thread_sleep(1000); @@ -1730,20 +1740,15 @@ static int srv_resolver_fallback_test(void) g_server[1].action = ACTION_CB; g_server[1].action_cb = &action2_1; - status = pj_dns_srv_resolve(&domain, &res_name, PORT2, pool, resolver, - PJ_DNS_SRV_FALLBACK_AAAA, - &cb_err, &srv_cb_2b, NULL); - if (status != PJ_SUCCESS) { - app_perror(" srv_resolve error", status); - return -80; - } + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, PORT2, pool, resolver, + PJ_DNS_SRV_FALLBACK_AAAA, + &cb_err, &srv_cb_2b, NULL), + NULL, return -640); pj_sem_wait(sem); - if (cb_err != 0) { - PJ_LOG(3,("test", " srv_resolve cb error, code=%d", cb_err)); - return -90; - } + PJ_TEST_EQ(cb_err, 0, "srv_resolve cb error", return -645); /* Clear cache */ pj_thread_sleep(1000); @@ -1766,11 +1771,13 @@ static void action3_1(const pj_dns_parsed_packet *pkt, pj_dns_parsed_packet *res; unsigned i; + lock(); res = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet); if (res->q == NULL) { res->q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query); } + unlock(); res->hdr.qdcount = 1; res->q[0].type = pkt->q[0].type; @@ -1782,8 +1789,10 @@ static void action3_1(const pj_dns_parsed_packet *pkt, pj_assert(pj_strcmp2(&pkt->q[0].name, "_sip._udp." DOMAIN3)==0); res->hdr.anscount = SRV_COUNT3; + lock(); res->ans = (pj_dns_parsed_rr*) pj_pool_calloc(pool, SRV_COUNT3, sizeof(pj_dns_parsed_rr)); + unlock(); for (i=0; ians[i].rdata.srv.weight = 2; res->ans[i].rdata.srv.port = (pj_uint16_t)(PORT3+i); + lock(); target = (char*)pj_pool_alloc(pool, 16); + unlock(); pj_ansi_snprintf(target, 16, "sip%02d." DOMAIN3, i); res->ans[i].rdata.srv.target = pj_str(target); } @@ -1806,8 +1817,10 @@ static void action3_1(const pj_dns_parsed_packet *pkt, //pj_assert(pj_strcmp2(&res->q[0].name, "sip." DOMAIN3)==0); res->hdr.anscount = A_COUNT3; + lock(); res->ans = (pj_dns_parsed_rr*) pj_pool_calloc(pool, A_COUNT3, sizeof(pj_dns_parsed_rr)); + unlock(); for (i=0; ians[i].type = PJ_DNS_TYPE_A; @@ -1856,7 +1869,6 @@ static void srv_cb_3(void *user_data, static int srv_resolver_many_test(void) { - pj_status_t status; pj_str_t domain = pj_str(DOMAIN3); pj_str_t res_name = pj_str("_sip._udp."); int cb_err = 0; @@ -1872,19 +1884,14 @@ static int srv_resolver_many_test(void) g_server[0].pkt_count = 0; g_server[1].pkt_count = 0; - status = pj_dns_srv_resolve(&domain, &res_name, 1, pool, resolver, PJ_TRUE, - &cb_err, &srv_cb_3, NULL); - if (status != PJ_SUCCESS) { - app_perror(" srv_resolve error", status); - return -10; - } + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, 1, pool, resolver, PJ_TRUE, + &cb_err, &srv_cb_3, NULL), + NULL, return -700); pj_sem_wait(sem); - if (cb_err != 0) { - PJ_LOG(3,("test", " srv_resolve cb error, code=%d", cb_err)); - return -20; - } + PJ_TEST_EQ(cb_err, 0, "srv_resolve cb error", return -710); return 0; } @@ -1897,34 +1904,42 @@ int resolver_test(void) { int rc; + PJ_LOG(3,(THIS_FILE, "init")); rc = init(PJ_FALSE); if (rc != 0) goto on_error; + PJ_LOG(3,(THIS_FILE, "a_parser_test")); rc = a_parser_test(); if (rc != 0) goto on_error; + PJ_LOG(3,(THIS_FILE, "addr_parser_test")); rc = addr_parser_test(); if (rc != 0) goto on_error; + PJ_LOG(3,(THIS_FILE, "simple_test")); rc = simple_test(); if (rc != 0) goto on_error; + PJ_LOG(3,(THIS_FILE, "dns_test")); rc = dns_test(); if (rc != 0) goto on_error; + PJ_LOG(3,(THIS_FILE, "srv_resolver_test")); rc = srv_resolver_test(); if (rc != 0) goto on_error; + PJ_LOG(3,(THIS_FILE, "srv_resolver_fallback_test")); rc = srv_resolver_fallback_test(); if (rc != 0) goto on_error; + PJ_LOG(3,(THIS_FILE, "srv_resolver_many_test")); rc = srv_resolver_many_test(); if (rc != 0) goto on_error; diff --git a/pjlib-util/src/pjlib-util-test/test.c b/pjlib-util/src/pjlib-util-test/test.c index f83e4f6946..cc519c949e 100644 --- a/pjlib-util/src/pjlib-util-test/test.c +++ b/pjlib-util/src/pjlib-util-test/test.c @@ -20,6 +20,14 @@ #include #include +#define THIS_FILE "test.c" + +pj_pool_factory *mem; +struct test_app_t test_app = { + .param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | + PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_INDENT, +}; + void app_perror(const char *msg, pj_status_t rc) { char errbuf[256]; @@ -30,80 +38,67 @@ void app_perror(const char *msg, pj_status_t rc) PJ_LOG(1,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf)); } -#define DO_TEST(test) do { \ - PJ_LOG(3, ("test", "Running %s...", #test)); \ - rc = test; \ - PJ_LOG(3, ("test", \ - "%s(%d)", \ - (char*)(rc ? "..ERROR" : "..success"), rc)); \ - if (rc!=0) goto on_return; \ - } while (0) - - -pj_pool_factory *mem; - -int param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | - PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_INDENT; - -static int test_inner(void) +static int test_inner(int argc, char *argv[]) { pj_caching_pool caching_pool; - int rc = 0; mem = &caching_pool.factory; pj_log_set_level(3); - pj_log_set_decor(param_log_decor); - - rc = pj_init(); - if (rc != 0) { - app_perror("pj_init() error!!", rc); - return rc; - } - - rc = pjlib_util_init(); - pj_assert(rc == 0); + pj_log_set_decor(test_app.param_log_decor); - pj_dump_config(); + PJ_TEST_SUCCESS(pj_init(), NULL, { return 1; }) + PJ_TEST_SUCCESS(pjlib_util_init(), NULL, { return 2; }); pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 ); + + if (ut_app_init1(&test_app.ut_app, mem) != PJ_SUCCESS) + return 1; + + if (test_app.ut_app.prm_config) + pj_dump_config(); #if INCLUDE_XML_TEST - DO_TEST(xml_test()); + UT_ADD_TEST(&test_app.ut_app, xml_test, 0); #endif #if INCLUDE_JSON_TEST - DO_TEST(json_test()); + UT_ADD_TEST(&test_app.ut_app, json_test, 0); #endif #if INCLUDE_ENCRYPTION_TEST - DO_TEST(encryption_test()); + UT_ADD_TEST(&test_app.ut_app, encryption_test, 0); # if WITH_BENCHMARK - DO_TEST(encryption_benchmark()); + UT_ADD_TEST(&test_app.ut_app, encryption_benchmark, 0); # endif #endif #if INCLUDE_STUN_TEST - DO_TEST(stun_test()); + UT_ADD_TEST(&test_app.ut_app, stun_test, 0); #endif #if INCLUDE_RESOLVER_TEST - DO_TEST(resolver_test()); + UT_ADD_TEST(&test_app.ut_app, resolver_test, 0); #endif #if INCLUDE_HTTP_CLIENT_TEST - DO_TEST(http_client_test()); + UT_ADD_TEST(&test_app.ut_app, http_client_test, 0); #endif -on_return: - return rc; + if (ut_run_tests(&test_app.ut_app, "pjlib-util tests", argc, argv)) { + ut_app_destroy(&test_app.ut_app); + return 1; + } + + ut_app_destroy(&test_app.ut_app); + return 0; } -int test_main(void) +int test_main(int argc, char *argv[]) { PJ_USE_EXCEPTION; PJ_TRY { - return test_inner(); + return test_inner(argc, argv); } PJ_CATCH_ANY { int id = PJ_GET_EXCEPTION(); diff --git a/pjlib-util/src/pjlib-util-test/test.h b/pjlib-util/src/pjlib-util-test/test.h index 9469e1f74f..d076b1eca8 100644 --- a/pjlib-util/src/pjlib-util-test/test.h +++ b/pjlib-util/src/pjlib-util-test/test.h @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #if defined(PJ_EXCLUDE_BENCHMARK_TESTS) && (PJ_EXCLUDE_BENCHMARK_TESTS==1) # define WITH_BENCHMARK 0 @@ -36,10 +37,19 @@ extern int json_test(void); extern int encryption_test(); extern int encryption_benchmark(); extern int stun_test(); -extern int test_main(void); +extern int test_main(int argc, char *argv[]); extern int resolver_test(void); extern int http_client_test(); extern void app_perror(const char *title, pj_status_t rc); extern pj_pool_factory *mem; +#define UT_MAX_TESTS 20 +#include "../../../pjlib/src/pjlib-test/test_util.h" + +struct test_app_t +{ + ut_app_t ut_app; + int param_log_decor; +}; +extern struct test_app_t test_app; diff --git a/pjlib/build/pjlib.vcproj b/pjlib/build/pjlib.vcproj index 3fb4575bde..bc4e2b2c75 100644 --- a/pjlib/build/pjlib.vcproj +++ b/pjlib/build/pjlib.vcproj @@ -12638,15 +12638,15 @@ Filter="h;hpp;hxx;hm;inl" > Header Files + + Header Files + Header Files @@ -448,8 +451,5 @@ Header Files - - Header Files - \ No newline at end of file diff --git a/pjlib/include/pj/config_site_test.h b/pjlib/include/pj/config_site_test.h index 8fb2599d66..6d81842d8e 100644 --- a/pjlib/include/pj/config_site_test.h +++ b/pjlib/include/pj/config_site_test.h @@ -1,3 +1,8 @@ +/* Temp workaround for MacOS rwmutex deadlock */ +#if defined(PJ_DARWINOS) && PJ_DARWINOS + #define PJ_EMULATE_RWMUTEX 1 +#endif + #define PJMEDIA_SRTP_HAS_DTLS 1 #define PJMEDIA_HAS_WEBRTC_AEC 1 #define PJMEDIA_CODEC_L16_HAS_8KHZ_MONO 1 diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index 428051b520..3536db9949 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -114,14 +114,35 @@ PJ_DEF(void) pj_test_suite_add_case(pj_test_suite *suite, pj_test_case *tc) pj_list_push_back(&suite->tests, tc); } +/* Own PRNG using Linear congruential generator because rand() yields + * difference sequence on different machines even with the same seed. + */ +static pj_uint32_t rand_int(pj_uint32_t seed) +{ +#define M ((pj_uint32_t)(1<<31)) +#define A ((pj_uint32_t)1103515245) +#define C ((pj_uint32_t)12345) + + return (pj_uint32_t)(((A*seed) + C) % M); + +#undef M +#undef A +#undef C +} + /* Shuffle */ PJ_DEF(void) pj_test_suite_shuffle(pj_test_suite *suite, int seed) { pj_test_case src, *tc; + pj_uint32_t rand; unsigned total, movable; - if (seed >= 0) - pj_srand(seed); + /* although pj_rand() is not used here, still call pj_srand() to make + * RNG used by other parts of the program repeatable. This should be + * the only call to pj_srand() in the whole program. + */ + pj_srand(seed); + rand = (pj_uint32_t)((seed >= 0) ? seed : pj_rand()); /* Move tests to new list */ pj_list_init(&src); @@ -147,10 +168,11 @@ PJ_DEF(void) pj_test_suite_shuffle(pj_test_suite *suite, int seed) /* Shuffle non KEEP_LAST tests */ while (movable > 0) { - int step = pj_rand() % total; - if (step < 0) - continue; - + unsigned step; + + rand = rand_int(rand); + step = rand % total; + for (tc=src.next; step>0; tc=tc->next, --step) ; diff --git a/pjlib/src/pjlib-test/activesock.c b/pjlib/src/pjlib-test/activesock.c index 29f8117667..98ce4f9688 100644 --- a/pjlib/src/pjlib-test/activesock.c +++ b/pjlib/src/pjlib-test/activesock.c @@ -31,6 +31,7 @@ #define THIS_FILE "activesock.c" +#define ERR(r__) { ret=r__; goto on_return; } /******************************************************************* * Simple UDP echo server. @@ -142,7 +143,8 @@ static void udp_echo_srv_destroy(struct udp_echo_srv *srv) * UDP ping pong test (send packet back and forth between two UDP echo * servers. */ -static int udp_ping_pong_test(void) +/* was udp_ping_pong_test(void) */ +static int activesock_test0(void) { pj_ioqueue_t *ioqueue = NULL; pj_pool_t *pool = NULL; @@ -153,27 +155,13 @@ static int udp_ping_pong_test(void) pj_status_t status; pool = pj_pool_create(mem, "pingpong", 512, 512, NULL); - if (!pool) - return -10; + PJ_TEST_NOT_NULL(pool, NULL, return -10); - status = pj_ioqueue_create(pool, 4, &ioqueue); - if (status != PJ_SUCCESS) { - ret = -20; - udp_echo_err("pj_ioqueue_create()", status); - goto on_return; - } - - status = udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv1); - if (status != PJ_SUCCESS) { - ret = -30; - goto on_return; - } - - status = udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv2); - if (status != PJ_SUCCESS) { - ret = -40; - goto on_return; - } + PJ_TEST_SUCCESS(pj_ioqueue_create(pool, 4, &ioqueue), NULL, ERR(-20)) + PJ_TEST_SUCCESS(udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv1), + NULL, ERR(-30)); + PJ_TEST_SUCCESS(udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv2), + NULL, ERR(-40)); /* initiate the first send */ for (count=0; count<1000; ++count) { @@ -193,11 +181,8 @@ static int udp_ping_pong_test(void) status = pj_activesock_sendto(srv1->asock, &srv1->send_key, &data, &sent, 0, &addr, sizeof(addr)); - if (status != PJ_SUCCESS && status != PJ_EPENDING) { - ret = -50; - udp_echo_err("sendto()", status); - goto on_return; - } + PJ_TEST_TRUE(status==PJ_SUCCESS || status==PJ_EPENDING, + "pj_activesock_sendto()", ERR(-50)); need_send = PJ_FALSE; } @@ -215,20 +200,10 @@ static int udp_ping_pong_test(void) #endif } - if (srv1->rx_err_cnt+srv1->tx_err_cnt != 0 || - srv2->rx_err_cnt+srv2->tx_err_cnt != 0) - { - /* Got error */ - ret = -60; - goto on_return; - } - - if (last_rx1 == srv1->rx_cnt && last_rx2 == srv2->rx_cnt) { - /* Packet lost */ - ret = -70; - udp_echo_err("packets have been lost", PJ_ETIMEDOUT); - goto on_return; - } + PJ_TEST_EQ(srv1->rx_err_cnt+srv1->tx_err_cnt, 0, NULL, ERR(-60)); + PJ_TEST_EQ(srv2->rx_err_cnt+srv2->tx_err_cnt, 0, NULL, ERR(-62)); + PJ_TEST_TRUE(last_rx1 != srv1->rx_cnt || last_rx2 != srv2->rx_cnt, + "timeout/packets have been lost", ERR(-70)); } ret = 0; @@ -331,7 +306,8 @@ static pj_bool_t tcp_on_data_sent(pj_activesock_t *asock, return PJ_TRUE; } -static int tcp_perf_test(void) +/* was tcp_perf_test() */ +static int activesock_test1(void) { enum { COUNT=10000 }; pj_pool_t *pool = NULL; @@ -341,48 +317,33 @@ static int tcp_perf_test(void) pj_activesock_cb cb; struct tcp_state *state1, *state2; unsigned i; - pj_status_t status; - - pool = pj_pool_create(mem, "tcpperf", 256, 256, NULL); + int ret = 0; + pj_status_t status = PJ_SUCCESS; - status = app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock1, - &sock2); - if (status != PJ_SUCCESS) { - status = -100; - goto on_return; - } + pool = pj_pool_create(mem, "activesock_test1", 256, 256, NULL); + PJ_TEST_NOT_NULL(pool, NULL, return -10); - status = pj_ioqueue_create(pool, 4, &ioqueue); - if (status != PJ_SUCCESS) { - status = -110; - goto on_return; - } + PJ_TEST_SUCCESS( app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock1, + &sock2), + NULL, ERR(-100)); + PJ_TEST_SUCCESS(pj_ioqueue_create(pool, 4, &ioqueue), + NULL, ERR(-110)); pj_bzero(&cb, sizeof(cb)); cb.on_data_read = &tcp_on_data_read; cb.on_data_sent = &tcp_on_data_sent; state1 = PJ_POOL_ZALLOC_T(pool, struct tcp_state); - status = pj_activesock_create(pool, sock1, pj_SOCK_STREAM(), NULL, ioqueue, - &cb, state1, &asock1); - if (status != PJ_SUCCESS) { - status = -120; - goto on_return; - } + PJ_TEST_SUCCESS(pj_activesock_create(pool, sock1, pj_SOCK_STREAM(), + NULL, ioqueue, &cb, state1, &asock1), + NULL, ERR(-120)); state2 = PJ_POOL_ZALLOC_T(pool, struct tcp_state); - status = pj_activesock_create(pool, sock2, pj_SOCK_STREAM(), NULL, ioqueue, - &cb, state2, &asock2); - if (status != PJ_SUCCESS) { - status = -130; - goto on_return; - } - - status = pj_activesock_start_read(asock1, pool, 1000, 0); - if (status != PJ_SUCCESS) { - status = -140; - goto on_return; - } + PJ_TEST_SUCCESS(pj_activesock_create(pool, sock2, pj_SOCK_STREAM(), NULL, + ioqueue, &cb, state2, &asock2), + NULL, ERR(-130)); + PJ_TEST_SUCCESS(pj_activesock_start_read(asock1, pool, 1000, 0), + NULL, ERR(-140)); /* Send packet as quickly as possible */ for (i=0; ierr && !state2->err; ++i) { @@ -420,16 +381,10 @@ static int tcp_perf_test(void) */ pj_symbianos_poll(-1, 0); #endif - if (status != PJ_SUCCESS) { - PJ_LOG(1,("", " err: send status=%d", status)); - status = -180; - break; - } else if (status == PJ_SUCCESS) { - if (len != sizeof(*pkt)) { - PJ_LOG(1,("", " err: shouldn't report partial sent")); - status = -190; - break; - } + PJ_TEST_SUCCESS(status, "send error", ERR(-180)); + if (status == PJ_SUCCESS) { + PJ_TEST_EQ(len, sizeof(*pkt), + "shouldn't report partial sent", ERR(-190)); } } @@ -458,23 +413,13 @@ static int tcp_perf_test(void) if (status == PJ_EPENDING) status = PJ_SUCCESS; - if (status != 0) - goto on_return; - - if (state1->err) { - status = -183; - goto on_return; - } - if (state2->err) { - status = -186; - goto on_return; - } - if (state1->next_recv_seq != COUNT) { - PJ_LOG(3,("", " err: only %u packets received, expecting %u", - state1->next_recv_seq, COUNT)); - status = -195; - goto on_return; - } + /* this should not happen. non-success should have been dealt with */ + PJ_TEST_SUCCESS(status, NULL, ERR(-182)); + + PJ_TEST_EQ(state1->err, 0, NULL, ERR(-183)); + PJ_TEST_EQ(state2->err, 0, NULL, ERR(-186)); + PJ_TEST_EQ(state1->next_recv_seq, COUNT, + "not all packets are received", ERR(-195)); on_return: if (asock2) @@ -486,24 +431,17 @@ static int tcp_perf_test(void) if (pool) pj_pool_release(pool); - return status; + return ret; } - - int activesock_test(void) { - int ret; - - PJ_LOG(3,("", "..udp ping/pong test")); - ret = udp_ping_pong_test(); - if (ret != 0) - return ret; + int rc; + if ((rc=activesock_test0()) != 0) + return rc; - PJ_LOG(3,("", "..tcp perf test")); - ret = tcp_perf_test(); - if (ret != 0) - return ret; + if ((rc=activesock_test1()) != 0) + return rc; return 0; } diff --git a/pjlib/src/pjlib-test/atomic.c b/pjlib/src/pjlib-test/atomic.c index 8bd0a5a222..83f99178c1 100644 --- a/pjlib/src/pjlib-test/atomic.c +++ b/pjlib/src/pjlib-test/atomic.c @@ -44,58 +44,48 @@ #if INCLUDE_ATOMIC_TEST +#define THIS_FILE "atomic.c" +#define ERR(r__) { rc=r__; goto on_return; } + int atomic_test(void) { pj_pool_t *pool; pj_atomic_t *atomic_var; - pj_status_t rc; - - pool = pj_pool_create(mem, NULL, 4096, 0, NULL); - if (!pool) - return -10; + int rc=0; + PJ_TEST_NOT_NULL( (pool=pj_pool_create(mem, NULL, 4096, 0, NULL)), + NULL, ERR(-10)); /* create() */ - rc = pj_atomic_create(pool, 111, &atomic_var); - if (rc != 0) { - return -20; - } + PJ_TEST_SUCCESS( pj_atomic_create(pool, 111, &atomic_var), NULL, ERR(-20)); /* get: check the value. */ - if (pj_atomic_get(atomic_var) != 111) - return -30; + PJ_TEST_EQ( pj_atomic_get(atomic_var), 111, NULL, ERR(-30)); /* increment. */ pj_atomic_inc(atomic_var); - if (pj_atomic_get(atomic_var) != 112) - return -40; + PJ_TEST_EQ( pj_atomic_get(atomic_var), 112, NULL, ERR(-40)); /* decrement. */ pj_atomic_dec(atomic_var); - if (pj_atomic_get(atomic_var) != 111) - return -50; + PJ_TEST_EQ( pj_atomic_get(atomic_var), 111, NULL, ERR(-50)); /* set */ pj_atomic_set(atomic_var, 211); - if (pj_atomic_get(atomic_var) != 211) - return -60; + PJ_TEST_EQ( pj_atomic_get(atomic_var), 211, NULL, ERR(-60)); /* add */ pj_atomic_add(atomic_var, 10); - if (pj_atomic_get(atomic_var) != 221) - return -60; + PJ_TEST_EQ( pj_atomic_get(atomic_var), 221, NULL, ERR(-60)); /* check the value again. */ - if (pj_atomic_get(atomic_var) != 221) - return -70; + PJ_TEST_EQ( pj_atomic_get(atomic_var), 221, NULL, ERR(-70)); /* destroy */ - rc = pj_atomic_destroy(atomic_var); - if (rc != 0) - return -80; + PJ_TEST_SUCCESS( pj_atomic_destroy(atomic_var), NULL, ERR(-80)); +on_return: pj_pool_release(pool); - - return 0; + return rc; } diff --git a/pjlib/src/pjlib-test/fifobuf.c b/pjlib/src/pjlib-test/fifobuf.c index d300452ce7..b6fd486867 100644 --- a/pjlib/src/pjlib-test/fifobuf.c +++ b/pjlib/src/pjlib-test/fifobuf.c @@ -85,8 +85,6 @@ static int fifobuf_rolling_test() PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, NULL, return -300); PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, NULL, return -310); - pj_srand(0); - /* Repeat the test */ for (rep=0; rep= start_time. */ @@ -118,11 +101,8 @@ static int file_test_internal(void) /* * Re-open the file and read data. */ - status = pj_file_open(NULL, FILENAME, PJ_O_RDONLY, &fd); - if (status != PJ_SUCCESS) { - app_perror("...file_open() error", status); - return -100; - } + PJ_TEST_SUCCESS(pj_file_open(NULL, FILENAME, PJ_O_RDONLY, &fd), + NULL, return -100); size = 0; while (size < (pj_ssize_t)sizeof(readbuf)) { @@ -133,6 +113,7 @@ static int file_test_internal(void) PJ_LOG(3,("", "...error reading file after %ld bytes " "(error follows)", size)); app_perror("...error", status); + pj_file_close(fd); return -110; } if (read == 0) { @@ -142,68 +123,41 @@ static int file_test_internal(void) size += read; } - if (size != sizeof(buffer)) - return -120; + PJ_TEST_EQ(size, sizeof(buffer), NULL, + {pj_file_close(fd); return -120; }); /* if (!pj_file_eof(fd, PJ_O_RDONLY)) return -130; */ - if (pj_memcmp(readbuf, buffer, size) != 0) - return -140; + PJ_TEST_EQ(pj_memcmp(readbuf, buffer, size), 0, NULL, + {pj_file_close(fd); return -140; }); /* Seek test. */ - status = pj_file_setpos(fd, 4, PJ_SEEK_SET); - if (status != PJ_SUCCESS) { - app_perror("...file_setpos() error", status); - return -141; - } + PJ_TEST_SUCCESS(pj_file_setpos(fd, 4, PJ_SEEK_SET), NULL, + {pj_file_close(fd); return -141; }); /* getpos test. */ - status = pj_file_getpos(fd, &pos); - if (status != PJ_SUCCESS) { - app_perror("...file_getpos() error", status); - return -142; - } - if (pos != 4) - return -143; + PJ_TEST_SUCCESS(pj_file_getpos(fd, &pos), NULL, + {pj_file_close(fd); return -142; }); + PJ_TEST_EQ(pos, 4, NULL, {pj_file_close(fd); return -143; }); - status = pj_file_close(fd); - if (status != PJ_SUCCESS) { - app_perror("...file_close() error", status); - return -150; - } + PJ_TEST_SUCCESS(pj_file_close(fd), NULL, return -150); /* * Rename test. */ - status = pj_file_move(FILENAME, NEWNAME); - if (status != PJ_SUCCESS) { - app_perror("...file_move() error", status); - return -160; - } - - if (pj_file_exists(FILENAME)) - return -170; - if (!pj_file_exists(NEWNAME)) - return -180; - - if (pj_file_size(NEWNAME) != sizeof(buffer)) - return -190; + PJ_TEST_SUCCESS(pj_file_move(FILENAME, NEWNAME), NULL, return -160); + PJ_TEST_EQ(pj_file_exists(FILENAME), 0, NULL, return -170); + PJ_TEST_TRUE(pj_file_exists(NEWNAME), NULL, return -180); + PJ_TEST_EQ(pj_file_size(NEWNAME), sizeof(buffer), NULL, return -190); /* Delete test. */ - status = pj_file_delete(NEWNAME); - if (status != PJ_SUCCESS) { - app_perror("...file_delete() error", status); - return -200; - } - - if (pj_file_exists(NEWNAME)) - return -210; + PJ_TEST_SUCCESS(pj_file_delete(NEWNAME), NULL, return -200); + PJ_TEST_EQ(pj_file_exists(NEWNAME), 0, NULL, return -210); - PJ_LOG(3,("", "...success")); - return PJ_SUCCESS; + return 0; } diff --git a/pjlib/src/pjlib-test/hash_test.c b/pjlib/src/pjlib-test/hash_test.c index fb59331309..05c093e87c 100644 --- a/pjlib/src/pjlib-test/hash_test.c +++ b/pjlib/src/pjlib-test/hash_test.c @@ -25,6 +25,8 @@ #if INCLUDE_HASH_TEST #define HASH_COUNT 31 +#define THIS_FILE "hash_test.c" + static int hash_test_with_key(pj_pool_t *pool, unsigned char key) { @@ -33,51 +35,28 @@ static int hash_test_with_key(pj_pool_t *pool, unsigned char key) pj_hash_iterator_t it_buf, *it; unsigned *entry; - ht = pj_hash_create(pool, HASH_COUNT); - if (!ht) - return -10; + PJ_TEST_NOT_NULL( (ht=pj_hash_create(pool, HASH_COUNT)), NULL, return -10); pj_hash_set(pool, ht, &key, sizeof(key), 0, &value); - entry = (unsigned*) pj_hash_get(ht, &key, sizeof(key), NULL); - if (!entry) - return -20; - - if (*entry != value) - return -30; - - if (pj_hash_count(ht) != 1) - return -30; - - it = pj_hash_first(ht, &it_buf); - if (it == NULL) - return -40; - - entry = (unsigned*) pj_hash_this(ht, it); - if (!entry) - return -50; - - if (*entry != value) - return -60; - - it = pj_hash_next(ht, it); - if (it != NULL) - return -70; + PJ_TEST_NOT_NULL((entry=(unsigned*)pj_hash_get(ht,&key,sizeof(key),NULL)), + NULL, return -20); + PJ_TEST_EQ( *entry, value, NULL, return -25); + PJ_TEST_EQ(pj_hash_count(ht), 1, NULL, return -30); + PJ_TEST_NOT_NULL((it=pj_hash_first(ht, &it_buf)), NULL, return -40); + PJ_TEST_NOT_NULL((entry=(unsigned*)pj_hash_this(ht, it)), NULL, + return -50); + PJ_TEST_EQ(*entry, value, NULL, return -60); + PJ_TEST_EQ(pj_hash_next(ht, it), NULL, NULL, return -70); /* Erase item */ pj_hash_set(NULL, ht, &key, sizeof(key), 0, NULL); - if (pj_hash_get(ht, &key, sizeof(key), NULL) != NULL) - return -80; - - if (pj_hash_count(ht) != 0) - return -90; - - it = pj_hash_first(ht, &it_buf); - if (it != NULL) - return -100; - + PJ_TEST_EQ(pj_hash_get(ht, &key, sizeof(key), NULL), NULL, NULL, + return -80); + PJ_TEST_EQ(pj_hash_count(ht), 0, NULL, return -90); + PJ_TEST_EQ(pj_hash_first(ht, &it_buf), NULL, NULL, return -100); return 0; } @@ -92,9 +71,7 @@ static int hash_collision_test(pj_pool_t *pool) unsigned char *values; unsigned i; - ht = pj_hash_create(pool, HASH_COUNT); - if (!ht) - return -200; + PJ_TEST_NOT_NULL((ht=pj_hash_create(pool, HASH_COUNT)), NULL, return -200); values = (unsigned char*) pj_pool_alloc(pool, COUNT); @@ -103,16 +80,13 @@ static int hash_collision_test(pj_pool_t *pool) pj_hash_set(pool, ht, &i, sizeof(i), 0, &values[i]); } - if (pj_hash_count(ht) != COUNT) - return -210; + PJ_TEST_EQ(pj_hash_count(ht), COUNT, NULL, return -210); for (i=0; iid = i; arg->ioqueue = ioqueue; - rc = pj_thread_create( pool, NULL, - &worker_thread, - arg, - PJ_THREAD_DEFAULT_STACK_SIZE, - PJ_THREAD_SUSPENDED, &thread[i] ); - if (rc != PJ_SUCCESS) { - app_perror("...error: unable to create thread", rc); - return -80; - } + PJ_TEST_SUCCESS( pj_thread_create( pool, NULL, + &worker_thread, + arg, + PJ_THREAD_DEFAULT_STACK_SIZE, + PJ_THREAD_SUSPENDED, &thread[i] ), + NULL, return -80); } /* Mark start time. */ - rc = pj_get_timestamp(&start); - if (rc != PJ_SUCCESS) - return -90; + PJ_TEST_SUCCESS( pj_get_timestamp(&start), NULL, return -90); /* Start the thread. */ TRACE_((THIS_FILE, " resuming all threads..")); for (i=0; i #define THIS_FILE "test_udp" -#define PORT 51233 #define LOOP 2 ///#define LOOP 2 #define BUF_MIN_SIZE 32 @@ -130,6 +129,7 @@ static int compliance_test(const pj_ioqueue_cfg *cfg) pj_sock_t ssock=-1, csock=-1; pj_sockaddr_in addr, dst_addr; int addrlen; + unsigned short port; pj_pool_t *pool = NULL; char *send_buf, *recv_buf; pj_ioqueue_t *ioque = NULL; @@ -169,11 +169,16 @@ static int compliance_test(const pj_ioqueue_cfg *cfg) TRACE_("bind socket..."); pj_bzero(&addr, sizeof(addr)); addr.sin_family = pj_AF_INET(); - addr.sin_port = pj_htons(PORT); if (pj_sock_bind(ssock, &addr, sizeof(addr))) { status=-10; goto on_error; } + // Get address + addrlen = sizeof(addr); + PJ_TEST_SUCCESS(pj_sock_getsockname(ssock, &addr, &addrlen), NULL, + {status=-15; goto on_error;}); + port = pj_sockaddr_get_port(&addr); + // Create I/O Queue. TRACE_("create ioqueue..."); rc = pj_ioqueue_create2(pool, PJ_IOQUEUE_MAX_HANDLES, cfg, &ioque); @@ -242,7 +247,7 @@ static int compliance_test(const pj_ioqueue_cfg *cfg) // Set destination address to send the packet. TRACE_("set destination address..."); temp = pj_str("127.0.0.1"); - if ((rc=pj_sockaddr_in_init(&dst_addr, &temp, PORT)) != 0) { + if ((rc=pj_sockaddr_in_init(&dst_addr, &temp, port)) != 0) { app_perror("...error: unable to resolve 127.0.0.1", rc); status=-290; goto on_error; } @@ -373,7 +378,6 @@ static void on_read_complete(pj_ioqueue_key_t *key, */ static int unregister_test(const pj_ioqueue_cfg *cfg) { - enum { RPORT = 50000, SPORT = 50001 }; pj_pool_t *pool; pj_ioqueue_t *ioqueue; pj_sock_t ssock; @@ -403,14 +407,14 @@ static int unregister_test(const pj_ioqueue_cfg *cfg) } /* Create sender socket */ - status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, SPORT, &ssock); + status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, -1, &ssock); if (status != PJ_SUCCESS) { app_perror("Error initializing socket", status); return -120; } /* Create receiver socket. */ - status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, RPORT, &rsock); + status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, -1, &rsock); if (status != PJ_SUCCESS) { app_perror("Error initializing socket", status); return -130; @@ -543,7 +547,7 @@ static int unregister_test(const pj_ioqueue_cfg *cfg) * Second stage of the test. Register another socket. Then unregister using * the previous key. */ - status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, RPORT, &rsock2); + status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, -1, &rsock2); if (status != PJ_SUCCESS) { app_perror("Error initializing socket (2)", status); return -330; @@ -791,16 +795,16 @@ static int parallel_recv_test(const pj_ioqueue_cfg *cfg) pool = pj_pool_create(mem, "test", 4000, 4000, NULL); if (!pool) { app_perror("Unable to create pool", PJ_ENOMEM); - return -100; + return -1100; } - CHECK(-110, app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, + CHECK(-1110, app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, &ssock, &csock)); - CHECK(-120, pj_ioqueue_create2(pool, 2, cfg, &ioqueue)); + CHECK(-1120, pj_ioqueue_create2(pool, 2, cfg, &ioqueue)); pj_bzero(&cb, sizeof(cb)); cb.on_read_complete = &on_read_complete2; - CHECK(-130, pj_ioqueue_register_sock(pool, ioqueue, ssock, &recv_packet_count, + CHECK(-1130, pj_ioqueue_register_sock(pool, ioqueue, ssock, &recv_packet_count, &cb, &skey)); /* spawn parallel recv()s */ @@ -809,7 +813,7 @@ static int parallel_recv_test(const pj_ioqueue_cfg *cfg) pj_ioqueue_op_key_init(&recv_ops[i], sizeof(pj_ioqueue_op_key_t)); recv_ops[i].user_data = &recv_datas[i]; recv_datas[i].len = sizeof(packet_t); - CHECK(-140, pj_ioqueue_recv(skey, &recv_ops[i], &recv_datas[i].buffer, + CHECK(-1140, pj_ioqueue_recv(skey, &recv_ops[i], &recv_datas[i].buffer, &recv_datas[i].len, 0)); } @@ -821,7 +825,7 @@ static int parallel_recv_test(const pj_ioqueue_cfg *cfg) arg->id = i; arg->timeout = TIMEOUT_SECS; - CHECK(-150, pj_thread_create(pool, "parallel_thread", + CHECK(-1150, pj_thread_create(pool, "parallel_thread", parallel_worker_thread, arg, 0, 0,&threads[i])); } @@ -843,7 +847,7 @@ static int parallel_recv_test(const pj_ioqueue_cfg *cfg) TRACE__((THIS_FILE, "......(was async sent)")); } else if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "......send error")); - retcode = -160; + retcode = -1160; goto on_return; } } @@ -856,8 +860,8 @@ static int parallel_recv_test(const pj_ioqueue_cfg *cfg) /* Wait until all threads quits */ for (i=0; i ASYNC_CNT+async_send) { PJ_LOG(3,(THIS_FILE, "....info: total wakeup count is %d " @@ -926,6 +930,7 @@ static int bench_test(const pj_ioqueue_cfg *cfg, int bufsize, { pj_sock_t ssock=-1, csock=-1; pj_sockaddr_in addr; + unsigned short port; pj_pool_t *pool = NULL; pj_sock_t *inactive_sock=NULL; pj_ioqueue_op_key_t *inactive_read_op; @@ -960,9 +965,14 @@ static int bench_test(const pj_ioqueue_cfg *cfg, int bufsize, // Bind server socket. pj_bzero(&addr, sizeof(addr)); addr.sin_family = pj_AF_INET(); - addr.sin_port = pj_htons(PORT); - if (pj_sock_bind(ssock, &addr, sizeof(addr))) - goto on_error; + PJ_TEST_SUCCESS(pj_sock_bind(ssock, &addr, sizeof(addr)), NULL, + goto on_error); + + // Get bound port + i = sizeof(addr); + PJ_TEST_SUCCESS(pj_sock_getsockname(ssock, &addr, &i), NULL, + goto on_error); + port = pj_sockaddr_get_port(&addr); pj_assert(inactive_sock_count+2 <= PJ_IOQUEUE_MAX_HANDLES); @@ -1034,7 +1044,7 @@ static int bench_test(const pj_ioqueue_cfg *cfg, int bufsize, } // Set destination address to send the packet. - pj_sockaddr_in_init(&addr, pj_cstr(&temp, "127.0.0.1"), PORT); + pj_sockaddr_in_init(&addr, pj_cstr(&temp, "127.0.0.1"), port); // Test loop. t_elapsed.u64 = 0; @@ -1241,7 +1251,7 @@ int udp_ioqueue_test() #endif }; pj_bool_t concurs[] = { PJ_TRUE, PJ_FALSE }; - int i, rc, err = 0; + int i, rc; for (i=0; i<(int)PJ_ARRAY_SIZE(epoll_flags); ++i) { pj_ioqueue_cfg cfg; @@ -1253,8 +1263,8 @@ int udp_ioqueue_test() pj_ioqueue_name(), cfg.epoll_flags)); rc = udp_ioqueue_test_imp(&cfg); - if (rc != 0 && err==0) - err = rc; + if (rc) return rc; + } for (i=0; i<(int)PJ_ARRAY_SIZE(concurs); ++i) { @@ -1267,8 +1277,7 @@ int udp_ioqueue_test() pj_ioqueue_name(), cfg.default_concurrency)); rc = udp_ioqueue_test_imp(&cfg); - if (rc != 0 && err==0) - err = rc; + if (rc) return rc; } #if PJ_HAS_THREADS @@ -1282,13 +1291,12 @@ int udp_ioqueue_test() pj_ioqueue_name(), cfg.epoll_flags)); rc = parallel_recv_test(&cfg); - if (rc != 0 && err==0) - err = rc; + if (rc) return rc; } #endif - return err; + return 0; } #else diff --git a/pjlib/src/pjlib-test/ioq_unreg.c b/pjlib/src/pjlib-test/ioq_unreg.c index 0894a3bb12..bed1e3b2b4 100644 --- a/pjlib/src/pjlib-test/ioq_unreg.c +++ b/pjlib/src/pjlib-test/ioq_unreg.c @@ -168,7 +168,7 @@ static int perform_unreg_test(pj_ioqueue_t *ioqueue, * will return from the poll early. */ if (other_socket) { - status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, 56127, &osd.sock); + status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, -1, &osd.sock); if (status != PJ_SUCCESS) { app_perror("Error creating other socket", status); return -12; diff --git a/pjlib/src/pjlib-test/main.c b/pjlib/src/pjlib-test/main.c index 43c29b00da..68cd44a6ef 100644 --- a/pjlib/src/pjlib-test/main.c +++ b/pjlib/src/pjlib-test/main.c @@ -21,14 +21,9 @@ #include #include #include +#include #include -extern int param_echo_sock_type; -extern const char *param_echo_server; -extern int param_echo_port; -extern pj_bool_t param_ci_mode; - - //#if defined(PJ_WIN32) && PJ_WIN32!=0 #if 0 #include @@ -82,55 +77,99 @@ static void init_signals(void) #define init_signals() #endif +static void usage() +{ + puts("Usage:"); + puts(" pjlib-test [OPTION] [test_to_run] [..]"); + puts(""); + puts("where OPTIONS:"); + puts(""); + puts(" -h, --help Show this help screen"); + + ut_usage(); + + puts(" --skip-e Skip essential tests"); + puts(" --ci-mode Running in slow CI mode"); + puts(" -i Ask ENTER before quitting"); + puts(" -n Do not trap signals"); + puts(" -p PORT Use port PORT for echo port"); + puts(" -s SERVER Use SERVER as ech oserver"); + puts(" -t ucp,tcp Set echo socket type to UDP or TCP"); +} + + int main(int argc, char *argv[]) { - int iarg=1, rc; + int rc; int interractive = 0; int no_trap = 0; boost(); + ut_app_init0(&test_app.ut_app); + + /* + * Parse arguments + */ + if (pj_argparse_get_bool(&argc, argv, "-h") || + pj_argparse_get_bool(&argc, argv, "--help")) + { + usage(); + return 0; + } + interractive = pj_argparse_get_bool(&argc, argv, "-i"); + no_trap = pj_argparse_get_bool(&argc, argv, "-n"); + if (pj_argparse_get_int(&argc, argv, "-p", &test_app.param_echo_port)) { + usage(); + return 1; + } + if (pj_argparse_get_str(&argc, argv, "-s", + (char**)&test_app.param_echo_server)) + { + usage(); + return 1; + } - while (iarg < argc) { - char *arg = argv[iarg++]; - - if (*arg=='-' && *(arg+1)=='i') { - interractive = 1; - - } else if (*arg=='-' && *(arg+1)=='n') { - no_trap = 1; - } else if (*arg=='-' && *(arg+1)=='p') { - pj_str_t port = pj_str(argv[iarg++]); - - param_echo_port = pj_strtoul(&port); - - } else if (*arg=='-' && *(arg+1)=='s') { - param_echo_server = argv[iarg++]; - - } else if (*arg=='-' && *(arg+1)=='t') { - pj_str_t type = pj_str(argv[iarg++]); - - if (pj_stricmp2(&type, "tcp")==0) - param_echo_sock_type = pj_SOCK_STREAM(); - else if (pj_stricmp2(&type, "udp")==0) - param_echo_sock_type = pj_SOCK_DGRAM(); + if (pj_argparse_exists(argv, "-t")) { + char *sock_type; + if (pj_argparse_get_str(&argc, argv, "-t", &sock_type)==PJ_SUCCESS) { + if (pj_ansi_stricmp(sock_type, "tcp")==0) + test_app.param_echo_sock_type = pj_SOCK_STREAM(); + else if (pj_ansi_stricmp(sock_type, "udp")==0) + test_app.param_echo_sock_type = pj_SOCK_DGRAM(); else { - printf("Error: unknown socket type %s\n", type.ptr); + printf("Error: unknown socket type %s for -t option\n", + sock_type); + usage(); return 1; } - } else if (strcmp(arg, "--ci-mode")==0) { - param_ci_mode = PJ_TRUE; - } else { - printf("Error in argument \"%s\"\n", arg); + usage(); return 1; } } + if (ut_parse_args(&test_app.ut_app, &argc, argv)) { + usage(); + return 1; + } + test_app.param_skip_essentials = pj_argparse_get_bool(&argc, argv, + "--skip-e"); + test_app.param_ci_mode = pj_argparse_get_bool(&argc, argv, "--ci-mode"); + + if (!no_trap) { init_signals(); } - rc = test_main(); + if (pj_argparse_peek_next_option(argv)) { + printf("Error: unknown argument %s\n", + pj_argparse_peek_next_option(argv)); + usage(); + return 1; + } + + /* argc/argv now contains option values only, if any */ + rc = test_main(argc, argv); if (interractive) { char s[10]; diff --git a/pjlib/src/pjlib-test/main_rtems.c b/pjlib/src/pjlib-test/main_rtems.c index 881b7b6d49..c382a12a4a 100644 --- a/pjlib/src/pjlib-test/main_rtems.c +++ b/pjlib/src/pjlib-test/main_rtems.c @@ -29,10 +29,6 @@ #include #include -extern int param_echo_sock_type; -extern const char *param_echo_server; -extern int param_echo_port; - #include #define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM diff --git a/pjlib/src/pjlib-test/main_win32.c b/pjlib/src/pjlib-test/main_win32.c index 1a89f41a15..b5487c5b5d 100644 --- a/pjlib/src/pjlib-test/main_win32.c +++ b/pjlib/src/pjlib-test/main_win32.c @@ -41,8 +41,6 @@ BOOL InitInstance (HINSTANCE, int); LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); -extern int param_log_decor; // in test.c - static HINSTANCE hInst; static HWND hwndLog; static HFONT hFixedFont; @@ -63,6 +61,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { MSG msg; + char *argv[] = {"pjlib-test", NULL}; PJ_UNUSED_ARG(lpCmdLine); PJ_UNUSED_ARG(hPrevInstance); @@ -72,10 +71,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, return FALSE; pj_log_set_log_func( &write_log ); - param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR; + test_app.param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR; // Run the test! - test_main(); + test_main(1, argv); PJ_LOG(3,(THIS_FILE,"")); PJ_LOG(3,(THIS_FILE,"Press ESC to quit")); diff --git a/pjlib/src/pjlib-test/rbtree.c b/pjlib/src/pjlib-test/rbtree.c index 7a96ac2855..846b4847c7 100644 --- a/pjlib/src/pjlib-test/rbtree.c +++ b/pjlib/src/pjlib-test/rbtree.c @@ -43,21 +43,13 @@ static int compare_node(const node_key *k1, const node_key *k2) } } -void randomize_string(char *str, int len) -{ - int i; - for (i=0; ikey,(node_key*)it->key)>=0) { - ++err; PJ_LOG(3, (THIS_FILE, "Error: %s >= %s", (char*)prev->user_data, (char*)it->user_data)); + rc=-45; + goto on_error; } } prev = it; @@ -124,13 +113,10 @@ static int test(void) // Search. for (j=0; j +#define THIS_FILE "test.c" + #ifdef _MSC_VER # pragma warning(disable:4127) @@ -32,185 +34,360 @@ #endif -#define DO_TEST(test) do { \ - PJ_LOG(3, ("test", "Running %s...", #test)); \ - rc = test; \ - PJ_LOG(3, ("test", \ - "%s(%d)", \ - (rc ? "..ERROR" : "..success"), rc)); \ - if (rc!=0) goto on_return; \ - } while (0) - - pj_pool_factory *mem; -int param_echo_sock_type; -const char *param_echo_server = ECHO_SERVER_ADDRESS; -int param_echo_port = ECHO_SERVER_START_PORT; -int param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | - PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_INDENT; -pj_bool_t param_ci_mode = PJ_FALSE; /* GH CI mode: more lenient tests */ +struct test_app_t test_app = { + .param_echo_sock_type = 0, + .param_echo_server = ECHO_SERVER_ADDRESS, + .param_echo_port = ECHO_SERVER_START_PORT, + .param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | + PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_INDENT, + .param_ci_mode = PJ_FALSE, +}; int null_func() { return 0; } -int test_inner(void) +static pj_bool_t test_included(const char *name, int argc, char *argv[]) { - pj_caching_pool caching_pool; - const char *filename; - int line; - int rc = 0; - - mem = &caching_pool.factory; + if (argc <= 1) + return PJ_TRUE; + + ++argv; + while (*argv) { + if (pj_ansi_strcmp(name, *argv)==0) + return PJ_TRUE; + ++argv; + } + return PJ_FALSE; +} - pj_log_set_level(3); - pj_log_set_decor(param_log_decor); +static pj_test_case *init_test_case( int (*test_func)(void), const char *obj_name, + unsigned flags, pj_test_case *tc, + char *log_buf, unsigned log_buf_size) +{ + flags |= PJ_TEST_FUNC_NO_ARG; + pj_test_case_init(tc, obj_name, flags, (int (*)(void*))test_func, NULL, + log_buf, log_buf_size, NULL); + return tc; +} - rc = pj_init(); - if (rc != 0) { - app_perror("pj_init() error!!", rc); - return rc; +static void list_tests(const pj_test_suite *suite, const char *title) +{ + unsigned d = pj_log_get_decor(); + const pj_test_case *tc; + + pj_log_set_decor(d ^ PJ_LOG_HAS_NEWLINE); + PJ_LOG(3,(THIS_FILE, "%ld %s:", pj_list_size(&suite->tests), title)); + pj_log_set_decor(0); + for (tc=suite->tests.next; tc!=&suite->tests; tc=tc->next) { + PJ_LOG(3,(THIS_FILE, " %s", tc->obj_name)); } + PJ_LOG(3,(THIS_FILE, "\n")); + pj_log_set_decor(d); +} - pj_dump_config(); - pj_caching_pool_init( &caching_pool, NULL, 0 ); +static pj_test_stat essential_tests(int argc, char *argv[]) +{ + pj_test_suite suite; + pj_test_runner runner; + pj_test_stat stat; + enum { + MAX_TESTS = 12, + LOG_BUF_SIZE = 256, + }; + char log_bufs[MAX_TESTS][LOG_BUF_SIZE]; + pj_test_case test_cases[MAX_TESTS]; + int ntests = 0; + + pj_bzero(&stat, sizeof(stat)); + /* Not necessary but to prevent unused-var warning when no test */ + pj_bzero(test_cases, sizeof(test_cases)); + pj_bzero(log_bufs, sizeof(log_bufs)); + + if (test_app.ut_app.prm_config) + pj_dump_config(); + + /* Test the unit-testing framework first, outside unit-test! + * Only perform the test if user is not requesting specific test. + */ + if (argc==1 && !test_app.ut_app.prm_list_test) { + PJ_LOG(3,(THIS_FILE, "Testing the unit-test framework (basic)")); + if (unittest_basic_test()) { + stat.nfailed = 1; + return stat; + } + } - if (param_ci_mode) - PJ_LOG(3,("test", "Using ci-mode")); + /* Now that the basic unit-testing framework has been tested, + * perform essential tests using basic unit-testing framework. + */ + pj_test_suite_init(&suite); + +#define ADD_TEST(test_func, flags) \ + if (ntests < MAX_TESTS) { \ + const char *test_name = #test_func; \ + if (test_included(test_name, argc, argv)) { \ + pj_test_case *tc = init_test_case( &test_func, test_name, flags, \ + &test_cases[ntests], \ + log_bufs[ntests], \ + LOG_BUF_SIZE); \ + pj_test_suite_add_case( &suite, tc); \ + ++ntests; \ + } \ + } else { \ + PJ_LOG(1,(THIS_FILE, "Too many tests for adding %s", #test_func)); \ + } #if INCLUDE_ERRNO_TEST - DO_TEST( errno_test() ); + ADD_TEST( errno_test, 0); #endif #if INCLUDE_EXCEPTION_TEST - DO_TEST( exception_test() ); + ADD_TEST( exception_test, 0); #endif #if INCLUDE_OS_TEST - DO_TEST( log_test() ); - DO_TEST( os_test() ); -#endif - -#if INCLUDE_RAND_TEST - DO_TEST( rand_test() ); + ADD_TEST( os_test, 0); #endif #if INCLUDE_LIST_TEST - DO_TEST( list_test() ); + ADD_TEST( list_test, 0); #endif #if INCLUDE_POOL_TEST - DO_TEST( pool_test() ); -#endif - -#if INCLUDE_POOL_PERF_TEST - DO_TEST( pool_perf_test() ); + ADD_TEST( pool_test, 0); #endif #if INCLUDE_STRING_TEST - DO_TEST( string_test() ); + ADD_TEST( string_test, 0); #endif #if INCLUDE_FIFOBUF_TEST - DO_TEST( fifobuf_test() ); + ADD_TEST( fifobuf_test, 0); +#endif + +#if INCLUDE_MUTEX_TEST + ADD_TEST( mutex_test, 0); +#endif + +#if INCLUDE_THREAD_TEST + ADD_TEST( thread_test, 0); +#endif + +#undef ADD_TEST + + if (test_app.ut_app.prm_list_test) { + list_tests(&suite, "essential tests"); + return stat; + } + + if (ntests > 0) { + pj_test_runner_param runner_prm; + pj_test_runner_param_default(&runner_prm); + runner_prm.stop_on_error = test_app.ut_app.prm_stop_on_error; + + PJ_LOG(3,(THIS_FILE, "Performing %d essential tests", ntests)); + pj_test_init_basic_runner(&runner, &runner_prm); + pj_test_run(&runner, &suite); + pj_test_display_log_messages(&suite, + test_app.ut_app.prm_logging_policy); + pj_test_get_stat(&suite, &stat); + pj_test_display_stat(&stat, "essential tests", THIS_FILE); + + if (stat.nfailed) + return stat; + } + + /* Now that the essential components have been tested, test the + * multithreaded unit-testing framework. + */ + if (argc==1) { + PJ_LOG(3,(THIS_FILE, "Testing the unit-test test scheduling")); + if (unittest_parallel_test()) { + stat.nfailed = 1; + return stat; + } + + PJ_LOG(3,(THIS_FILE, "Testing the unit-test framework (multithread)")); + if (unittest_test()) { + stat.nfailed = 1; + return stat; + } + } + + return stat; +} + +static int features_tests(int argc, char *argv[]) +{ + if (ut_app_init1(&test_app.ut_app, mem) != PJ_SUCCESS) + return 1; + +#if INCLUDE_RAND_TEST + UT_ADD_TEST(&test_app.ut_app, rand_test, 0); #endif -#if INCLUDE_UNITTEST_TEST - DO_TEST( unittest_basic_test() ); - DO_TEST( unittest_test() ); - DO_TEST( unittest_parallel_test() ); +#if INCLUDE_POOL_PERF_TEST + UT_ADD_TEST(&test_app.ut_app, pool_perf_test, 0); #endif #if INCLUDE_RBTREE_TEST - DO_TEST( rbtree_test() ); + UT_ADD_TEST(&test_app.ut_app, rbtree_test, 0); #endif #if INCLUDE_HASH_TEST - DO_TEST( hash_test() ); + UT_ADD_TEST(&test_app.ut_app, hash_test, 0); #endif #if INCLUDE_TIMESTAMP_TEST - DO_TEST( timestamp_test() ); + UT_ADD_TEST(&test_app.ut_app, timestamp_test, 0); #endif #if INCLUDE_ATOMIC_TEST - DO_TEST( atomic_test() ); -#endif - -#if INCLUDE_MUTEX_TEST - DO_TEST( mutex_test() ); + UT_ADD_TEST(&test_app.ut_app, atomic_test, 0); #endif #if INCLUDE_TIMER_TEST - DO_TEST( timer_test() ); + UT_ADD_TEST(&test_app.ut_app, timer_test, 0); #endif #if INCLUDE_SLEEP_TEST - DO_TEST( sleep_test() ); + UT_ADD_TEST(&test_app.ut_app, sleep_test, 0); #endif -#if INCLUDE_THREAD_TEST - DO_TEST( thread_test() ); +#if INCLUDE_FILE_TEST + UT_ADD_TEST(&test_app.ut_app, file_test, 0); #endif #if INCLUDE_SOCK_TEST - DO_TEST( sock_test() ); + UT_ADD_TEST(&test_app.ut_app, sock_test, 0); #endif #if INCLUDE_SOCK_PERF_TEST - DO_TEST( sock_perf_test() ); + UT_ADD_TEST(&test_app.ut_app, sock_perf_test, 0); #endif #if INCLUDE_SELECT_TEST - DO_TEST( select_test() ); + UT_ADD_TEST(&test_app.ut_app, select_test, 0); #endif #if INCLUDE_UDP_IOQUEUE_TEST - DO_TEST( udp_ioqueue_test() ); + UT_ADD_TEST(&test_app.ut_app, udp_ioqueue_test, 0); #endif #if PJ_HAS_TCP && INCLUDE_TCP_IOQUEUE_TEST - DO_TEST( tcp_ioqueue_test() ); + UT_ADD_TEST(&test_app.ut_app, tcp_ioqueue_test, 0); #endif -#if INCLUDE_IOQUEUE_UNREG_TEST - DO_TEST( udp_ioqueue_unreg_test() ); + /* Consistently encountered retcode 520 on Windows virtual machine + with 8 vcpu and 16GB RAM when multithread unit test is used, + with the following logs: + 17:50:57.761 .tcp (multithreads) + 17:50:58.254 ...pj_ioqueue_send() error: Object is busy (PJ_EBUSY) + 17:50:58.264 ..test failed (retcode=520) + 17:50:58.264 .tcp (multithreads, sequenced, concur=0) + 17:51:06.084 .tcp (multithreads, sequenced, concur=1) + 17:51:06.484 ...pj_ioqueue_send() error: Object is busy (PJ_EBUSY) + 17:51:06.486 ..test failed (retcode=520) + + I suspect it's because the ioq stress test also uses a lot of threads + and couldn't keep up with processing the data. + Therefore we'll disable parallelism on Windows for this test. [blp] + */ +#if INCLUDE_IOQUEUE_STRESS_TEST +# if defined(PJ_WIN32) && PJ_WIN32!=0 + UT_ADD_TEST(&test_app.ut_app, ioqueue_stress_test, PJ_TEST_EXCLUSIVE); +# else + UT_ADD_TEST(&test_app.ut_app, ioqueue_stress_test, 0); +# endif #endif -#if INCLUDE_IOQUEUE_STRESS_TEST - DO_TEST( ioqueue_stress_test() ); +#if INCLUDE_IOQUEUE_UNREG_TEST + UT_ADD_TEST(&test_app.ut_app, udp_ioqueue_unreg_test, 0); #endif #if INCLUDE_IOQUEUE_PERF_TEST - DO_TEST( ioqueue_perf_test() ); + UT_ADD_TEST(&test_app.ut_app, ioqueue_perf_test0, 0); + UT_ADD_TEST(&test_app.ut_app, ioqueue_perf_test1, 0); #endif #if INCLUDE_ACTIVESOCK_TEST - DO_TEST( activesock_test() ); -#endif - -#if INCLUDE_FILE_TEST - DO_TEST( file_test() ); + UT_ADD_TEST(&test_app.ut_app, activesock_test, 0); #endif #if INCLUDE_SSLSOCK_TEST - DO_TEST( ssl_sock_test() ); + UT_ADD_TEST(&test_app.ut_app, ssl_sock_test, 0); #endif + +#undef ADD_TEST + + if (ut_run_tests(&test_app.ut_app, "features tests", argc, argv)) { + ut_app_destroy(&test_app.ut_app); + return 1; + } + + ut_app_destroy(&test_app.ut_app); + return 0; +} + +int test_inner(int argc, char *argv[]) +{ + pj_caching_pool caching_pool; + pj_test_stat stat; + const char *filename; + int line; + int rc = 0; + + mem = &caching_pool.factory; + + pj_log_set_level(3); + pj_log_set_decor(test_app.param_log_decor); + + rc = pj_init(); + if (rc != 0) { + app_perror("pj_init() error!!", rc); + return rc; + } + + pj_caching_pool_init( &caching_pool, NULL, 0 ); + + if (test_app.param_ci_mode) + PJ_LOG(3,(THIS_FILE, "Using ci-mode")); + + if (!test_app.param_skip_essentials) { + stat = essential_tests(argc, argv); + if (stat.nfailed) { + rc = 1; + goto on_return; + } + } + + if (argc-1 > 0 && stat.nruns==argc-1) { + /* cmdline specifies test(s) to run, and the number of runs + * matches that. That means all requested tests have been run. + */ + } else { + rc = features_tests(argc, argv); + if (rc) + goto on_return; + } + #if INCLUDE_ECHO_SERVER //echo_server(); //echo_srv_sync(); udp_echo_srv_ioqueue(); #elif INCLUDE_ECHO_CLIENT - if (param_echo_sock_type == 0) - param_echo_sock_type = pj_SOCK_DGRAM(); + if (test_app.param_echo_sock_type == 0) + test_app.param_echo_sock_type = pj_SOCK_DGRAM(); - echo_client( param_echo_sock_type, - param_echo_server, - param_echo_port); + echo_client( test_app.param_echo_sock_type, + test_app.param_echo_server, + test_app.param_echo_port); #endif goto on_return; @@ -219,16 +396,16 @@ int test_inner(void) pj_caching_pool_destroy( &caching_pool ); - PJ_LOG(3,("test", " ")); + PJ_LOG(3,(THIS_FILE, " ")); pj_thread_get_stack_info(pj_thread_this(), &filename, &line); - PJ_LOG(3,("test", "Stack max usage: %u, deepest: %s:%u", + PJ_LOG(3,(THIS_FILE, "Stack max usage: %u, deepest: %s:%u", pj_thread_get_stack_max_usage(pj_thread_this()), filename, line)); if (rc == 0) - PJ_LOG(3,("test", "Looks like everything is okay!..")); + PJ_LOG(3,(THIS_FILE, "Looks like everything is okay!..")); else - PJ_LOG(3,("test", "Test completed with error(s)")); + PJ_LOG(3,(THIS_FILE, "**Test completed with error(s)**")); pj_shutdown(); @@ -237,16 +414,16 @@ int test_inner(void) #include -int test_main(void) +int test_main(int argc, char *argv[]) { PJ_USE_EXCEPTION; PJ_TRY { - return test_inner(); + return test_inner(argc, argv); } PJ_CATCH_ANY { int id = PJ_GET_EXCEPTION(); - PJ_LOG(3,("test", "FATAL: unhandled exception id %d (%s)", + PJ_LOG(3,(THIS_FILE, "FATAL: unhandled exception id %d (%s)", id, pj_exception_id_name(id))); } PJ_END; diff --git a/pjlib/src/pjlib-test/test.h b/pjlib/src/pjlib-test/test.h index 226a71bf84..7a109a5fe2 100644 --- a/pjlib/src/pjlib-test/test.h +++ b/pjlib/src/pjlib-test/test.h @@ -20,6 +20,8 @@ #define __PJLIB_TEST_H__ #include +#include +#include #define TEST_DEFAULT 1 @@ -51,7 +53,7 @@ #define INCLUDE_FIFOBUF_TEST GROUP_DATA_STRUCTURE #define INCLUDE_RBTREE_TEST GROUP_DATA_STRUCTURE #define INCLUDE_TIMER_TEST GROUP_DATA_STRUCTURE -#define INCLUDE_UNITTEST_TEST GROUP_DATA_STRUCTUREc +#define INCLUDE_UNITTEST_TEST GROUP_DATA_STRUCTURE #define INCLUDE_ATOMIC_TEST GROUP_OS #define INCLUDE_MUTEX_TEST (PJ_HAS_THREADS && GROUP_OS) #define INCLUDE_SLEEP_TEST GROUP_OS @@ -109,11 +111,15 @@ extern int select_test(void); extern int udp_ioqueue_test(void); extern int udp_ioqueue_unreg_test(void); extern int tcp_ioqueue_test(void); -extern int ioqueue_perf_test(void); +extern int ioqueue_perf_test0(void); +extern int ioqueue_perf_test1(void); extern int ioqueue_stress_test(void); extern int activesock_test(void); extern int file_test(void); extern int ssl_sock_test(void); +extern int unittest_basic_test(void); +extern int unittest_parallel_test(void); +extern int unittest_test(void); extern int echo_server(void); extern int echo_client(int sock_type, const char *server, int port); @@ -122,10 +128,25 @@ extern int echo_srv_sync(void); extern int udp_echo_srv_ioqueue(void); extern int echo_srv_common_loop(pj_atomic_t *bytes_counter); +#define UT_MAX_TESTS 24 +#include "test_util.h" +/* Global vars */ extern pj_pool_factory *mem; - -extern int test_main(void); +struct test_app_t +{ + ut_app_t ut_app; + + int param_echo_sock_type; + const char *param_echo_server; + int param_echo_port; + int param_log_decor; + pj_bool_t param_ci_mode; + pj_bool_t param_skip_essentials; +}; +extern struct test_app_t test_app; + +extern int test_main(int argc, char *argv[]); extern void app_perror(const char *msg, pj_status_t err); extern pj_status_t app_socket(int family, int type, int proto, int port, pj_sock_t *ptr_sock); diff --git a/pjlib/src/pjlib-test/test_util.h b/pjlib/src/pjlib-test/test_util.h new file mode 100644 index 0000000000..7eb4575c7e --- /dev/null +++ b/pjlib/src/pjlib-test/test_util.h @@ -0,0 +1,317 @@ +#include +#include +#include +#include +#include +#include +#include + +/* Overrideable max tests */ +#ifndef UT_MAX_TESTS +# define UT_MAX_TESTS 16 +#endif + +/* Overrideable log max buffer size */ +#ifndef UT_LOG_BUF_SIZE +# define UT_LOG_BUF_SIZE 1000 +#endif + +/* Usually app won't supply THIS_FILE when including test_util.h, so + * create a temporary one + */ +#ifndef THIS_FILE +# define THIS_FILE "test.c" +# define UNDEF_THIS_FILE 1 +#endif + +/* Unit testing app */ +typedef struct ut_app_t +{ + pj_bool_t prm_config; + pj_test_select_tests prm_logging_policy; + int prm_nthreads; + int prm_list_test; + pj_bool_t prm_stop_on_error; + pj_bool_t prm_shuffle; + int prm_seed; + unsigned flags; + unsigned verbosity; + + pj_pool_t *pool; + pj_test_suite suite; + pj_test_runner *runner; + + int ntests; + pj_test_case test_cases[UT_MAX_TESTS]; +} ut_app_t; + +/* Call this in main.c before parsing arguments */ +PJ_INLINE(void) ut_app_init0(ut_app_t *ut_app) +{ + pj_bzero(ut_app, sizeof(*ut_app)); + ut_app->prm_logging_policy = PJ_TEST_FAILED_TESTS; + ut_app->prm_nthreads = -1; + ut_app->flags = 0; +} + +/* Call this in test.c before adding test cases */ +PJ_INLINE(pj_status_t) ut_app_init1(ut_app_t *ut_app, pj_pool_factory *mem) +{ + ut_app->pool = pj_pool_create(mem, THIS_FILE, 4000, 4000, NULL); + PJ_TEST_NOT_NULL(ut_app->pool, NULL, return PJ_ENOMEM); + pj_test_suite_init(&ut_app->suite); + return PJ_SUCCESS; +} + +/* Don't forget to call this */ +PJ_INLINE(void) ut_app_destroy(ut_app_t *ut_app) +{ + pj_pool_release(ut_app->pool); + ut_app->pool = NULL; +} + +typedef int (*ut_func)(void*); + +/* This is for adding test func that has no arg */ +#define UT_ADD_TEST(ut_app, test_func, flags) \ + ut_add_test(ut_app, (ut_func)test_func, 0, \ + #test_func, flags | PJ_TEST_FUNC_NO_ARG, argc, argv) + + +/* This is for adding test func that HAS arg */ +#define UT_ADD_TEST1(ut_app, test_func, arg, flags) \ + ut_add_test(ut_app, (ut_func)test_func, arg, #test_func, flags, argc, argv) + + +/* Check if a test is specified/requested in cmdline */ +PJ_INLINE(pj_bool_t) ut_test_included(const char *name, int argc, char *argv[]) +{ + if (argc <= 1) + return PJ_TRUE; + + ++argv; + while (*argv) { + if (pj_ansi_strcmp(name, *argv)==0) + return PJ_TRUE; + ++argv; + } + return PJ_FALSE; +} + +/* Add test case */ +PJ_INLINE(pj_status_t) ut_add_test(ut_app_t *ut_app, int (*test_func)(void*), + void *arg, const char *test_name, + unsigned flags, int argc, char *argv[]) +{ + char *log_buf; + pj_test_case *tc; + + if (ut_app->ntests >= UT_MAX_TESTS) { + PJ_LOG(1,(THIS_FILE, "Too many tests for adding %s", test_name)); + return PJ_ETOOMANY; + } + + if (!ut_test_included(test_name, argc, argv)) { + return PJ_ENOTFOUND; + } + + log_buf = (char*)pj_pool_alloc(ut_app->pool, UT_LOG_BUF_SIZE); + tc = &ut_app->test_cases[ut_app->ntests]; + flags |= ut_app->flags; + pj_test_case_init(tc, test_name, flags, (int (*)(void*))test_func, arg, + log_buf, UT_LOG_BUF_SIZE, NULL); + + pj_test_suite_add_case( &ut_app->suite, tc); + ++ut_app->ntests; + + return PJ_SUCCESS; +} + +PJ_INLINE(void) ut_list_tests(ut_app_t *ut_app, const char *title) +{ + unsigned d = pj_log_get_decor(); + const pj_test_case *tc, *prev=NULL; + + pj_log_set_decor(d ^ PJ_LOG_HAS_NEWLINE); + PJ_LOG(3,(THIS_FILE, "%ld %s:", pj_list_size(&ut_app->suite.tests), + title)); + pj_log_set_decor(0); + for (tc=ut_app->suite.tests.next; tc!=&ut_app->suite.tests; tc=tc->next) { + if (!prev || pj_ansi_strcmp(tc->obj_name, prev->obj_name)) + PJ_LOG(3,(THIS_FILE, " %s", tc->obj_name)); + prev = tc; + } + PJ_LOG(3,(THIS_FILE, "\n")); + pj_log_set_decor(d); +} + +PJ_INLINE(pj_status_t) ut_run_tests(ut_app_t *ut_app, const char *title, + int argc, char *argv[]) +{ + pj_test_runner_param runner_prm; + pj_test_runner_param_default(&runner_prm); + pj_test_runner *runner; + pj_test_stat stat; + pj_status_t status; + + if (ut_app->prm_shuffle) { + PJ_LOG(3,(THIS_FILE, "Shuffling tests, random seed=%d", + ut_app->prm_seed)); + pj_test_suite_shuffle(&ut_app->suite, ut_app->prm_seed); + } + + if (ut_app->prm_list_test) { + ut_list_tests(ut_app, title); + return PJ_SUCCESS; + } + + if (argc > 1) { + int i; + for (i=1; isuite.tests.next; tc!=&ut_app->suite.tests; + tc=tc->next) + { + if (pj_ansi_strcmp(argv[i], tc->obj_name)==0) + break; + } + if (tc==&ut_app->suite.tests) { + PJ_LOG(2,(THIS_FILE, "Test \"%s\" is not found in %s", + argv[i], title)); + } + } + } + + if (ut_app->ntests <= 0) + return PJ_SUCCESS; + + pj_test_runner_param_default(&runner_prm); + runner_prm.stop_on_error = ut_app->prm_stop_on_error; + if (ut_app->prm_nthreads >= 0) + runner_prm.nthreads = ut_app->prm_nthreads; + runner_prm.verbosity = ut_app->verbosity; + status = pj_test_create_text_runner(ut_app->pool, &runner_prm, &runner); + PJ_TEST_SUCCESS(status, "error creating text runner", return status); + + PJ_LOG(3,(THIS_FILE, + "Performing %d %s with %d worker thread%s", + ut_app->ntests, title, runner_prm.nthreads, + runner_prm.nthreads>1?"s":"")); + + pj_test_run(runner, &ut_app->suite); + pj_test_runner_destroy(runner); + pj_test_display_log_messages(&ut_app->suite, ut_app->prm_logging_policy); + pj_test_get_stat(&ut_app->suite, &stat); + pj_test_display_stat(&stat, title, THIS_FILE); + + return stat.nfailed ? PJ_EBUG : PJ_SUCCESS; +} + +PJ_INLINE(void) ut_usage() +{ + puts(" -c, --config Show configuration macros"); + puts(" -l 0,1,2,3 0: Don't show logging after tests"); + puts(" 1: Show logs of only failed tests (default)"); + puts(" 2: Show logs of only successful tests"); + puts(" 3: Show logs of all tests"); + puts(" --log-no-cache Do not cache logging"); + printf(" -w N Set N worker threads (0: disable. Default: %d)\n", + PJ_HAS_THREADS); + puts(" -L, --list List the tests and exit"); + puts(" --stop-err Stop testing on error"); + puts(" --shuffle Shuffle the test order"); + puts(" --seed N Set shuffle random seed (must be >= 0)"); + puts(" --stdout-buf N Set stdout buffering mode:"); + puts(" --stderr-buf N Set stderr buffering mode:"); + puts(" 0: unbufferred (default for stderr)"); + puts(" 1: line"); + puts(" 2: fully bufferred (default for stdout)"); + puts(" -v, --verbose Show info when starting/stopping tests"); +} + + +PJ_INLINE(pj_status_t) ut_parse_args(ut_app_t *ut_app, int *argc, char *argv[]) +{ + int itmp = -1; + pj_status_t status; + + ut_app->prm_config = pj_argparse_get_bool(argc, argv, "-c") || + pj_argparse_get_bool(argc, argv, "--config"); + ut_app->prm_list_test = pj_argparse_get_bool(argc, argv, "-L") || + pj_argparse_get_bool(argc, argv, "--list"); + ut_app->prm_stop_on_error = pj_argparse_get_bool(argc, argv, "--stop-err"); + ut_app->prm_shuffle = pj_argparse_get_bool(argc, argv, "--shuffle"); + if (pj_argparse_get_bool(argc, argv, "--log-no-cache")) { + ut_app->flags |= PJ_TEST_LOG_NO_CACHE; + } + + if (pj_argparse_exists(argv, "-l")) { + status = pj_argparse_get_int(argc, argv, "-l", &itmp); + if (status==PJ_SUCCESS && itmp>=0 && itmp<=3) { + ut_app->prm_logging_policy = (pj_test_select_tests)itmp; + } else { + puts("Error: invalid value for -l option"); + return PJ_EINVAL; + } + } + + if (pj_argparse_exists(argv, "-w")) { + status = pj_argparse_get_int(argc, argv, "-w", &itmp); + if (status==PJ_SUCCESS && itmp>=0 && itmp<50) { + ut_app->prm_nthreads = itmp; + } else { + puts("Error: invalid/missing value for -w option"); + return PJ_EINVAL; + } + } + + if (ut_app->prm_shuffle) { + pj_time_val tv; + + status = pj_gettimeofday(&tv); + if (status != PJ_SUCCESS) + return status; + + ut_app->prm_seed = (int)(tv.msec); + status = pj_argparse_get_int(argc, argv, "--seed", &ut_app->prm_seed); + if (status != PJ_SUCCESS) + return status; + } + + ut_app->verbosity = pj_argparse_get_bool(argc, argv, "-v") || + pj_argparse_get_bool(argc, argv, "--verbose"); + + itmp = -101; + if (pj_argparse_get_int(argc, argv, "--stdout-buf", &itmp)==PJ_SUCCESS && + itmp != -101) + { + switch (itmp) { + case 0: setvbuf(stdout, NULL, _IONBF, 0); break; + case 1: setvbuf(stdout, NULL, _IOLBF, 0); break; + case 2: setvbuf(stdout, NULL, _IOFBF, 0); break; + default: + printf("Error: invalid --stdout-buf value %d\n", itmp); + return PJ_EINVAL; + } + } + + itmp = -101; + if (pj_argparse_get_int(argc, argv, "--stderr-buf", &itmp)==PJ_SUCCESS && + itmp != -101) + { + switch (itmp) { + case 0: setvbuf(stderr, NULL, _IONBF, 0); break; + case 1: setvbuf(stderr, NULL, _IOLBF, 0); break; + case 2: setvbuf(stderr, NULL, _IOFBF, 0); break; + default: + printf("Error: invalid --stderr-buf value %d\n", itmp); + return PJ_EINVAL; + } + } + + return PJ_SUCCESS; +} + +#ifdef UNDEF_THIS_FILE +# undef THIS_FILE +#endif diff --git a/pjlib/src/pjlib-test/thread.c b/pjlib/src/pjlib-test/thread.c index 47867bb8b8..eb996ee99b 100644 --- a/pjlib/src/pjlib-test/thread.c +++ b/pjlib/src/pjlib-test/thread.c @@ -52,6 +52,8 @@ #define THIS_FILE "thread_test" +typedef unsigned long counter_t; +#define counter_fmt "%lu" static volatile int quit_flag=0; #if 0 @@ -74,7 +76,7 @@ static int thread_proc(void *data) pj_thread_t *this_thread; unsigned id; pj_status_t rc; - pj_uint32_t *pcounter = (pj_uint32_t *)data; + counter_t *pcounter = (counter_t *)data; id = *pcounter; PJ_UNUSED_ARG(id); /* Warning about unused var if TRACE__ is disabled */ @@ -105,7 +107,9 @@ static int thread_proc(void *data) for (;!quit_flag;) { (*pcounter)++; //Must sleep if platform doesn't do time-slicing. - //pj_thread_sleep(0); + //2024-12-18: always sleep since otherwise test may occasionaly throw + // error on Linux (bennylp) + pj_thread_sleep(0); } TRACE__((THIS_FILE, " thread %d quitting..", id)); @@ -120,7 +124,7 @@ static int simple_thread(const char *title, unsigned flags) pj_pool_t *pool; pj_thread_t *thread; pj_status_t rc; - pj_uint32_t counter = 0; + counter_t counter = 0; PJ_LOG(3,(THIS_FILE, "..%s", title)); @@ -187,7 +191,7 @@ static int timeslice_test(void) { enum { NUM_THREADS = 4 }; pj_pool_t *pool; - pj_uint32_t counter[NUM_THREADS], lowest, highest, diff; + counter_t counter[NUM_THREADS], lowest, highest, diff; pj_thread_t *thread[NUM_THREADS]; unsigned i; pj_status_t rc; @@ -273,9 +277,8 @@ static int timeslice_test(void) /* Now examine the value of the counters. * Check that all threads had equal proportion of processing. */ - lowest = 0xFFFFFFFF; - highest = 0; - for (i=0; i highest) @@ -295,12 +298,14 @@ static int timeslice_test(void) PJ_LOG(3,(THIS_FILE, "...ERROR: thread didn't have equal timeslice!")); PJ_LOG(3,(THIS_FILE, - ".....lowest counter=%u, highest counter=%u, diff=%u%%", + ".....lowest counter=" counter_fmt + ", highest counter=" counter_fmt + ", diff=" counter_fmt "%%", lowest, highest, diff)); return -80; } else { PJ_LOG(3,(THIS_FILE, - "...info: timeslice diff between lowest & highest=%u%%", + "...info: timeslice diff between lowest & highest=" counter_fmt "%%", diff)); } diff --git a/pjlib/src/pjlib-test/timer.c b/pjlib/src/pjlib-test/timer.c index 7acfefaaf8..2f9b9f597b 100644 --- a/pjlib/src/pjlib-test/timer.c +++ b/pjlib/src/pjlib-test/timer.c @@ -94,8 +94,6 @@ static int test_timer_heap(void) pj_timestamp t1, t2, t_sched, t_cancel, t_poll; pj_time_val now, expire; - pj_gettimeofday(&now); - pj_srand(now.sec); t_sched.u32.lo = t_cancel.u32.lo = t_poll.u32.lo = 0; // Register timers @@ -485,7 +483,6 @@ static int timer_stress_test(void) pj_thread_t **poll_threads = NULL; pj_thread_t **cancel_threads = NULL; struct thread_param tparam = {0}; - pj_time_val now; #if SIMULATE_CRASH pj_timer_entry *entry; pj_pool_t *tmp_pool; @@ -494,9 +491,6 @@ static int timer_stress_test(void) PJ_LOG(3,("test", "...Stress test")); - pj_gettimeofday(&now); - pj_srand(now.sec); - pool = pj_pool_create( mem, NULL, 128, 128, NULL); if (!pool) { PJ_LOG(3,("test", "...error: unable to create pool")); diff --git a/pjmedia/src/test/codec_vectors.c b/pjmedia/src/test/codec_vectors.c index f561354d2d..f5d121f950 100644 --- a/pjmedia/src/test/codec_vectors.c +++ b/pjmedia/src/test/codec_vectors.c @@ -579,7 +579,7 @@ int codec_test_vectors(void) unsigned i; pj_status_t status; - status = pjmedia_endpt_create(mem, NULL, 0, &endpt); + status = pjmedia_endpt_create2(mem, NULL, 0, &endpt); if (status != PJ_SUCCESS) return -5; @@ -588,7 +588,7 @@ int codec_test_vectors(void) #if PJMEDIA_HAS_G7221_CODEC status = pjmedia_codec_g7221_init(endpt); if (status != PJ_SUCCESS) { - pjmedia_endpt_destroy(endpt); + pjmedia_endpt_destroy2(endpt); return -7; } @@ -635,7 +635,7 @@ int codec_test_vectors(void) if (pj_file_exists(TMP_OUT)) pj_file_delete(TMP_OUT); - pjmedia_endpt_destroy(endpt); + pjmedia_endpt_destroy2(endpt); return rc_final; } diff --git a/pjmedia/src/test/jbuf_test.c b/pjmedia/src/test/jbuf_test.c index 82aabf79ad..c8c08e891a 100644 --- a/pjmedia/src/test/jbuf_test.c +++ b/pjmedia/src/test/jbuf_test.c @@ -26,10 +26,12 @@ #define JB_MAX_PREFETCH 10 #define JB_PTIME 20 #define JB_BUF_SIZE 50 +#define THIS_FILE "jbuf_test.c" //#define REPORT //#define PRINT_COMMENT + typedef struct test_param_t { pj_bool_t adaptive; unsigned init_prefetch; @@ -79,10 +81,16 @@ static pj_bool_t parse_test_headers(char *line, test_param_t *param, cond->lost = cond_val; } else if (*p == '=') { + char *newline_pos; + /* Test title. */ ++p; while (*p && isspace(*p)) ++p; - printf("%s", p); + + if ((newline_pos=strchr(p, '\n')) != NULL) + *newline_pos = '\0'; + + PJ_LOG(3, (THIS_FILE, "--- %s ---", p)); } else if (*p == '#') { /* Ignore comment line. */ @@ -121,15 +129,15 @@ static pj_bool_t process_test_data(char data, pjmedia_jbuf *jb, case 'L': /* Lost */ *last_seq = *seq; ++*seq; - printf("Lost\n"); + PJ_LOG(3,(THIS_FILE, "Lost")); break; case 'R': /* Sequence restarts */ *seq = 1; - printf("Sequence restarting, from %u to %u\n", *last_seq, *seq); + PJ_LOG(3,(THIS_FILE, "Sequence restarting, from %u to %u", *last_seq, *seq)); break; case 'J': /* Sequence jumps */ (*seq) += 20; - printf("Sequence jumping, from %u to %u\n", *last_seq, *seq); + PJ_LOG(3,(THIS_FILE, "Sequence jumping, from %u to %u", *last_seq, *seq)); break; case 'D': /* Frame duplicated */ pjmedia_jbuf_put_frame(jb, (void*)frame, 1, *seq - 1); @@ -142,7 +150,7 @@ static pj_bool_t process_test_data(char data, pjmedia_jbuf *jb, break; default: print_state = PJ_FALSE; - printf("Unknown test data '%c'\n", data); + PJ_LOG(3,(THIS_FILE, "Unknown test data '%c'", data)); break; } @@ -154,8 +162,8 @@ static pj_bool_t process_test_data(char data, pjmedia_jbuf *jb, pjmedia_jb_state state; pjmedia_jbuf_get_state(jb, &state); - printf("seq=%d\t%c\tsize=%d\tprefetch=%d\n", - *last_seq, toupper(data), state.size, state.prefetch); + PJ_LOG(3,(THIS_FILE, ("seq=%d\t%c\tsize=%d\tprefetch=%d", + *last_seq, toupper(data), state.size, state.prefetch)); } #else PJ_UNUSED_ARG(print_state); /* Warning about variable set but unused */ @@ -164,7 +172,7 @@ static pj_bool_t process_test_data(char data, pjmedia_jbuf *jb, return PJ_TRUE; } -int jbuf_main(void) +int jbuf_test(void) { FILE *input; pj_bool_t data_eof = PJ_FALSE; @@ -194,10 +202,8 @@ int jbuf_main(void) } /* Failed to open test data file. */ - if (input == NULL) { - printf("Failed to open test data file, Jbtest.dat\n"); - return -1; - } + PJ_TEST_NOT_NULL(input, "failed to open test data file, Jbtest.dat", + return -1); old_log_level = pj_log_get_level(); pj_log_set_level(5); @@ -226,7 +232,7 @@ int jbuf_main(void) cond.empty = -1; cond.lost = -1; - printf("\n\n"); + PJ_LOG(3,(THIS_FILE, "%s", "")); /* Parse test session title, param, and conditions */ do { @@ -237,8 +243,6 @@ int jbuf_main(void) if (p == NULL) break; - //printf("======================================================\n"); - /* Initialize test session */ pool = pj_pool_create(mem, "JBPOOL", 256*16, 256*16, NULL); pjmedia_jbuf_create(pool, &jb_name, 1, JB_PTIME, JB_BUF_SIZE, &jb); @@ -255,9 +259,9 @@ int jbuf_main(void) #ifdef REPORT pjmedia_jbuf_get_state(jb, &state); - printf("Initial\tsize=%d\tprefetch=%d\tmin.pftch=%d\tmax.pftch=%d\n", - state.size, state.prefetch, state.min_prefetch, - state.max_prefetch); + PJ_LOG(3,(THIS_FILE, (("Initial\tsize=%d\tprefetch=%d\tmin.pftch=%d\tmax.pftch=%d", + state.size, state.prefetch, state.min_prefetch, + state.max_prefetch)); #endif @@ -285,7 +289,7 @@ int jbuf_main(void) if (c == '#') { #ifdef PRINT_COMMENT while (*p && isspace(*p)) ++p; - if (*p) printf("..%s", p); + if (*p) printf(("..%s", p)); #endif *p = 0; continue; @@ -298,44 +302,44 @@ int jbuf_main(void) /* Print JB states */ pjmedia_jbuf_get_state(jb, &state); - printf("------------------------------------------------------\n"); - printf("Summary:\n"); - printf(" size=%d prefetch=%d\n", state.size, state.prefetch); - printf(" delay (min/max/avg/dev)=%d/%d/%d/%d ms\n", - state.min_delay, state.max_delay, state.avg_delay, - state.dev_delay); - printf(" lost=%d discard=%d empty=%d burst(avg)=%d\n", - state.lost, state.discard, state.empty, state.avg_burst); + //PJ_LOG(3,(THIS_FILE, "------------------------------------------------------")); + PJ_LOG(3,(THIS_FILE, "Summary:")); + PJ_LOG(3,(THIS_FILE, " size=%d prefetch=%d", state.size, state.prefetch)); + PJ_LOG(3,(THIS_FILE, " delay (min/max/avg/dev)=%d/%d/%d/%d ms", + state.min_delay, state.max_delay, state.avg_delay, + state.dev_delay)); + PJ_LOG(3,(THIS_FILE, " lost=%d discard=%d empty=%d burst(avg)=%d", + state.lost, state.discard, state.empty, state.avg_burst)); /* Evaluate test session */ if (cond.burst >= 0 && (int)state.avg_burst > cond.burst) { - printf("! 'Burst' should be %d, it is %d\n", - cond.burst, state.avg_burst); + PJ_LOG(1,(THIS_FILE, "! 'Burst' should be %d, it is %d", + cond.burst, state.avg_burst)); rc |= 1; } if (cond.delay >= 0 && (int)state.avg_delay/JB_PTIME > cond.delay) { - printf("! 'Delay' should be %d, it is %d\n", - cond.delay, state.avg_delay/JB_PTIME); + PJ_LOG(1,(THIS_FILE, "! 'Delay' should be %d, it is %d", + cond.delay, state.avg_delay/JB_PTIME)); rc |= 2; } if (cond.delay_min >= 0 && (int)state.min_delay/JB_PTIME > cond.delay_min) { - printf("! 'Minimum delay' should be %d, it is %d\n", - cond.delay_min, state.min_delay/JB_PTIME); + PJ_LOG(1,(THIS_FILE, "! 'Minimum delay' should be %d, it is %d", + cond.delay_min, state.min_delay/JB_PTIME)); rc |= 32; } if (cond.discard >= 0 && (int)state.discard > cond.discard) { - printf("! 'Discard' should be %d, it is %d\n", - cond.discard, state.discard); + PJ_LOG(3,(THIS_FILE, "! 'Discard' should be %d, it is %d", + cond.discard, state.discard)); rc |= 4; } if (cond.empty >= 0 && (int)state.empty > cond.empty) { - printf("! 'Empty' should be %d, it is %d\n", - cond.empty, state.empty); + PJ_LOG(3,(THIS_FILE, "! 'Empty' should be %d, it is %d", + cond.empty, state.empty)); rc |= 8; } if (cond.lost >= 0 && (int)state.lost > cond.lost) { - printf("! 'Lost' should be %d, it is %d\n", - cond.lost, state.lost); + PJ_LOG(3,(THIS_FILE, "! 'Lost' should be %d, it is %d", + cond.lost, state.lost)); rc |= 16; } diff --git a/pjmedia/src/test/main.c b/pjmedia/src/test/main.c index 77b49700cf..f0ee9ca799 100644 --- a/pjmedia/src/test/main.c +++ b/pjmedia/src/test/main.c @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #include "test.h" @@ -33,19 +34,19 @@ #if (PJ_LINUX || PJ_DARWINOS) && defined(PJ_HAS_EXECINFO_H) && PJ_HAS_EXECINFO_H != 0 -#include +#include #include -#include -#include -#include +#include +#include +#include static void print_stack(int sig) { - void *array[16]; - size_t size; - - size = backtrace(array, 16); - fprintf(stderr, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, STDERR_FILENO); + void *array[16]; + size_t size; + + size = backtrace(array, 16); + fprintf(stderr, "Error: signal %d:\n", sig); + backtrace_symbols_fd(array, size, STDERR_FILENO); exit(1); } @@ -59,6 +60,21 @@ static void init_signals(void) #define init_signals() #endif +static void usage() +{ + puts("Usage:"); + puts(" pjmedia-test [OPTIONS] [test_to_run] [test to run] [..]"); + puts(""); + puts("where OPTIONS:"); + puts(""); + puts(" -h, --help Show this help screen"); + + ut_usage(); + + puts(" -i Ask ENTER before quitting"); + puts(" -n Do not trap signals"); +} + static int main_func(int argc, char *argv[]) { @@ -66,22 +82,25 @@ static int main_func(int argc, char *argv[]) int interractive = 0; int no_trap = 0; - while (argc > 1) { - char *arg = argv[--argc]; + if (pj_argparse_get_bool(&argc, argv, "-h") || + pj_argparse_get_bool(&argc, argv, "--help")) + { + usage(); + return 0; + } - if (*arg=='-' && *(arg+1)=='i') { - interractive = 1; + ut_app_init0(&test_app.ut_app); - } else if (*arg=='-' && *(arg+1)=='n') { - no_trap = 1; - } - } + interractive = pj_argparse_get_bool(&argc, argv, "-i"); + no_trap = pj_argparse_get_bool(&argc, argv, "-n"); + if (ut_parse_args(&test_app.ut_app, &argc, argv)) + return 1; if (!no_trap) { init_signals(); } - rc = test_main(); + rc = test_main(argc, argv); if (interractive) { char s[10]; diff --git a/pjmedia/src/test/mips_test.c b/pjmedia/src/test/mips_test.c index 25f89bfcbe..69391787b1 100644 --- a/pjmedia/src/test/mips_test.c +++ b/pjmedia/src/test/mips_test.c @@ -731,7 +731,7 @@ static pj_status_t codec_on_destroy(struct pjmedia_port *this_port) pjmedia_codec_mgr_dealloc_codec(pjmedia_endpt_get_codec_mgr(cp->endpt), cp->codec); cp->codec_deinit(); - pjmedia_endpt_destroy(cp->endpt); + pjmedia_endpt_destroy2(cp->endpt); return PJ_SUCCESS; } @@ -764,7 +764,7 @@ static pjmedia_port* codec_encode_decode( pj_pool_t *pool, cp->base.on_destroy = &codec_on_destroy; cp->codec_deinit = codec_deinit; - status = pjmedia_endpt_create(mem, NULL, 0, &cp->endpt); + status = pjmedia_endpt_create2(mem, NULL, 0, &cp->endpt); if (status != PJ_SUCCESS) return NULL; @@ -1699,7 +1699,7 @@ static void stream_port_custom_deinit(struct test_entry *te) pjmedia_stream_destroy(sp->stream); pjmedia_transport_close(sp->transport); sp->codec_deinit(); - pjmedia_endpt_destroy(sp->endpt); + pjmedia_endpt_destroy2(sp->endpt); } @@ -1743,7 +1743,7 @@ static pjmedia_port* create_stream( pj_pool_t *pool, te->custom_deinit = &stream_port_custom_deinit; sp->codec_deinit = codec_deinit; - status = pjmedia_endpt_create(mem, NULL, 0, &sp->endpt); + status = pjmedia_endpt_create2(mem, NULL, 0, &sp->endpt); if (status != PJ_SUCCESS) return NULL; diff --git a/pjmedia/src/test/test.c b/pjmedia/src/test/test.c index 5023bdf6f0..a7eb075851 100644 --- a/pjmedia/src/test/test.c +++ b/pjmedia/src/test/test.c @@ -20,17 +20,8 @@ #define THIS_FILE "test.c" -#define DO_TEST(test) do { \ - PJ_LOG(3, (THIS_FILE, "Running %s...", #test)); \ - rc = test; \ - PJ_LOG(3, (THIS_FILE, \ - "%s(%d)", \ - (rc ? "..ERROR" : "..success"), rc)); \ - if (rc!=0) goto on_return; \ - } while (0) - - pj_pool_factory *mem; +struct test_app_t test_app; void app_perror(pj_status_t status, const char *msg) @@ -53,15 +44,21 @@ void *dummy() } #endif -int test_main(void) +int test_main(int argc, char *argv[]) { int rc = 0; pj_caching_pool caching_pool; - pj_pool_t *pool; + pj_pool_t *pool = NULL; + + PJ_TEST_SUCCESS(pj_init(), NULL, return 1); + + if (test_app.ut_app.prm_config) + pj_dump_config(); - pj_init(); pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0); - pool = pj_pool_create(&caching_pool.factory, "test", 1000, 512, NULL); + PJ_TEST_NOT_NULL(pool=pj_pool_create(&caching_pool.factory, "test", + 1000, 512, NULL), + NULL, {rc=10; goto on_return;}); pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_INDENT); @@ -69,43 +66,59 @@ int test_main(void) mem = &caching_pool.factory; - pjmedia_event_mgr_create(pool, 0, NULL); + PJ_TEST_SUCCESS(pjmedia_event_mgr_create(pool, 0, NULL), + NULL, {rc=30; goto on_return;}); #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) - pjmedia_video_format_mgr_create(pool, 64, 0, NULL); - pjmedia_converter_mgr_create(pool, NULL); - pjmedia_vid_codec_mgr_create(pool, NULL); + PJ_TEST_SUCCESS(pjmedia_video_format_mgr_create(pool, 64, 0, NULL), + NULL, {rc=50; goto on_return;}); + PJ_TEST_SUCCESS(pjmedia_converter_mgr_create(pool, NULL), + NULL, {rc=60; goto on_return;}); + PJ_TEST_SUCCESS(pjmedia_vid_codec_mgr_create(pool, NULL), + NULL, {rc=70; goto on_return;}); #endif -#if HAS_VID_PORT_TEST - DO_TEST(vid_port_test()); -#endif + PJ_TEST_SUCCESS(ut_app_init1(&test_app.ut_app, mem), + NULL, {rc=40; goto on_return;}); -#if HAS_VID_DEV_TEST - DO_TEST(vid_dev_test()); +#if HAS_MIPS_TEST + /* Run in exclusive mode to get the best performance */ + UT_ADD_TEST(&test_app.ut_app, mips_test, PJ_TEST_EXCLUSIVE); #endif #if HAS_VID_CODEC_TEST - DO_TEST(vid_codec_test()); + /* Run in exclusive mode due to device sharing error? */ + UT_ADD_TEST(&test_app.ut_app, vid_codec_test, PJ_TEST_EXCLUSIVE); +#endif + +#if HAS_VID_PORT_TEST + UT_ADD_TEST(&test_app.ut_app, vid_port_test, 0); +#endif + +#if HAS_VID_DEV_TEST + UT_ADD_TEST(&test_app.ut_app, vid_dev_test, 0); #endif #if HAS_SDP_NEG_TEST - DO_TEST(sdp_neg_test()); + UT_ADD_TEST(&test_app.ut_app, sdp_neg_test, 0); #endif //DO_TEST(sdp_test (&caching_pool.factory)); //DO_TEST(rtp_test(&caching_pool.factory)); //DO_TEST(session_test (&caching_pool.factory)); #if HAS_JBUF_TEST - DO_TEST(jbuf_main()); -#endif -#if HAS_MIPS_TEST - DO_TEST(mips_test()); + UT_ADD_TEST(&test_app.ut_app, jbuf_test, 0); #endif #if HAS_CODEC_VECTOR_TEST - DO_TEST(codec_test_vectors()); + UT_ADD_TEST(&test_app.ut_app, codec_test_vectors, 0); #endif - PJ_LOG(3,(THIS_FILE," ")); + if (ut_run_tests(&test_app.ut_app, "pjmedia tests", argc, argv)) { + rc = 99; + } else { + rc = 0; + } + + ut_app_destroy(&test_app.ut_app); on_return: if (rc != 0) { @@ -121,8 +134,8 @@ int test_main(void) #endif pjmedia_event_mgr_destroy(pjmedia_event_mgr_instance()); - - pj_pool_release(pool); + if (pool) + pj_pool_release(pool); pj_caching_pool_destroy(&caching_pool); return rc; diff --git a/pjmedia/src/test/test.h b/pjmedia/src/test/test.h index a4d2b48dcc..848ae97eda 100644 --- a/pjmedia/src/test/test.h +++ b/pjmedia/src/test/test.h @@ -41,7 +41,7 @@ int session_test(void); int rtp_test(void); int sdp_test(void); -int jbuf_main(void); +int jbuf_test(void); int sdp_neg_test(void); int mips_test(void); int codec_test_vectors(void); @@ -52,6 +52,16 @@ int vid_port_test(void); extern pj_pool_factory *mem; void app_perror(pj_status_t status, const char *title); -int test_main(void); +int test_main(int argc, char *argv[]); + +#define UT_MAX_TESTS 80 +#include "../../../pjlib/src/pjlib-test/test_util.h" + +struct test_app_t +{ + ut_app_t ut_app; +}; +extern struct test_app_t test_app; + #endif /* __PJMEDIA_TEST_H__ */ diff --git a/pjmedia/src/test/vid_codec_test.c b/pjmedia/src/test/vid_codec_test.c index 286f2f96b0..4d2c2402df 100644 --- a/pjmedia/src/test/vid_codec_test.c +++ b/pjmedia/src/test/vid_codec_test.c @@ -200,6 +200,7 @@ static int encode_decode_test(pj_pool_t *pool, const char *codec_id, char codec_name[5]; pj_status_t status; int rc = 0; + unsigned i, count; switch (packing) { case PJMEDIA_VID_PACKING_PACKETS: @@ -268,10 +269,28 @@ static int encode_decode_test(pj_pool_t *pool, const char *codec_id, cap_idx = CAPTURE_DEV; #endif - /* Lookup SDL renderer */ - status = pjmedia_vid_dev_lookup("SDL", "SDL renderer", &rdr_idx); - if (status != PJ_SUCCESS) { - rc = 207; goto on_return; + /* Lookup renderer */ + rdr_idx = PJMEDIA_VID_INVALID_DEV; + count = pjmedia_vid_dev_count(); + for (i = 0; i < count; ++i) { + pjmedia_vid_dev_info cdi; + + status = pjmedia_vid_dev_get_info(i, &cdi); + if (status != PJ_SUCCESS) + rc = 211; goto on_return; + + /* Only interested with render device */ + if ((cdi.dir & PJMEDIA_DIR_RENDER) != 0) { + rdr_idx = i; + break; + } + } + if (rdr_idx == PJMEDIA_VID_INVALID_DEV) { + PJ_LOG(3, (THIS_FILE, "Unable to find renderer device")); + /* We may be on a machine that doesn't have access to a renderer + * device, don't fail the test. + */ + rc = 0; goto on_return; } /* Prepare codec */ diff --git a/pjnath/src/pjnath-test/concur_test.c b/pjnath/src/pjnath-test/concur_test.c index 54ddb7b6c7..31d575f5f9 100644 --- a/pjnath/src/pjnath-test/concur_test.c +++ b/pjnath/src/pjnath-test/concur_test.c @@ -226,14 +226,14 @@ static int stun_destroy_test_session(struct stun_test_session *test_sess) static int stun_destroy_test(void) { - enum { LOOP = 500 }; +#define ERR(errval) { rc=errval; goto on_return; } + enum { LOOP = 10 }; struct stun_test_session test_sess; pj_sockaddr bind_addr; int addr_len; pj_caching_pool cp; pj_pool_t *pool; unsigned i; - pj_status_t status; int rc = 0; PJ_LOG(3,(THIS_FILE, " STUN destroy concurrency test")); @@ -241,53 +241,61 @@ static int stun_destroy_test(void) pj_bzero(&test_sess, sizeof(test_sess)); pj_caching_pool_init(&cp, NULL, 0); - pool = pj_pool_create(&cp.factory, "testsess", 512, 512, NULL); + PJ_TEST_NOT_NULL((pool=pj_pool_create(&cp.factory, "testsess", + 512, 512, NULL)), NULL, ERR(-10)); pj_stun_config_init(&test_sess.stun_cfg, &cp.factory, 0, NULL, NULL); - status = pj_timer_heap_create(pool, 1023, &test_sess.stun_cfg.timer_heap); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_timer_heap_create(pool, 1023, + &test_sess.stun_cfg.timer_heap), + NULL, ERR(-20)); - status = pj_lock_create_recursive_mutex(pool, NULL, &test_sess.lock); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_lock_create_recursive_mutex(pool, NULL, + &test_sess.lock), + NULL, ERR(-30)); - pj_timer_heap_set_lock(test_sess.stun_cfg.timer_heap, test_sess.lock, PJ_TRUE); - pj_assert(status == PJ_SUCCESS); + pj_timer_heap_set_lock(test_sess.stun_cfg.timer_heap, test_sess.lock, + PJ_TRUE); - status = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &test_sess.stun_cfg.ioqueue); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, + &test_sess.stun_cfg.ioqueue), + NULL, ERR(-40)); - pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &test_sess.server_sock); + PJ_TEST_SUCCESS(pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, + &test_sess.server_sock), + NULL, ERR(-50)); pj_sockaddr_init(pj_AF_INET(), &bind_addr, NULL, 0); - status = pj_sock_bind(test_sess.server_sock, &bind_addr, pj_sockaddr_get_len(&bind_addr)); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_sock_bind(test_sess.server_sock, &bind_addr, + pj_sockaddr_get_len(&bind_addr)), + NULL, ERR(-60)); /* Set socket to nonblocking to avoid stuck in recv/recvfrom() on concurrent events */ app_set_sock_nb(test_sess.server_sock); addr_len = sizeof(bind_addr); - status = pj_sock_getsockname(test_sess.server_sock, &bind_addr, &addr_len); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_sock_getsockname(test_sess.server_sock, &bind_addr, + &addr_len), + NULL, ERR(-70)); test_sess.server_port = pj_sockaddr_get_port(&bind_addr); - status = pj_event_create(pool, NULL, PJ_TRUE, PJ_FALSE, &test_sess.server_event); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_event_create(pool, NULL, PJ_TRUE, PJ_FALSE, + &test_sess.server_event), + NULL, ERR(-80)); for (i=0; icfg.enable_stun & YES) { unsigned stun_idx = ice_cfg->stun_tp_cnt++; @@ -168,7 +173,7 @@ static void set_stun_turn_cfg(struct ice_ept *ept, } else { ice_cfg->stun_tp[stun_idx].server = pj_str(serverip); } - ice_cfg->stun_tp[stun_idx].port = STUN_SERVER_PORT; + ice_cfg->stun_tp[stun_idx].port = stun_server_port; ice_cfg->stun_tp[stun_idx].af = GET_AF(use_ipv6); } @@ -189,7 +194,7 @@ static void set_stun_turn_cfg(struct ice_ept *ept, } else { ice_cfg->turn_tp[turn_idx].server = pj_str(serverip); } - ice_cfg->turn_tp[turn_idx].port = TURN_SERVER_PORT; + ice_cfg->turn_tp[turn_idx].port = turn_server_port; ice_cfg->turn_tp[turn_idx].conn_type = PJ_TURN_TP_UDP; ice_cfg->turn_tp[turn_idx].auth_cred.type = PJ_STUN_AUTH_CRED_STATIC; ice_cfg->turn_tp[turn_idx].auth_cred.data.static_cred.realm = @@ -221,19 +226,15 @@ static int create_ice_strans(struct test_sess *test_sess, pj_sockaddr hostip; char serveripv4[PJ_INET6_ADDRSTRLEN]; char serveripv6[PJ_INET6_ADDRSTRLEN]; - pj_status_t status; unsigned flag = (ept->cfg.client_flag)?ept->cfg.client_flag:CLIENT_IPV4; - status = pj_gethostip(pj_AF_INET(), &hostip); - if (status != PJ_SUCCESS) - return -1030; + PJ_TEST_SUCCESS(pj_gethostip(pj_AF_INET(), &hostip), NULL, return -1030); pj_sockaddr_print(&hostip, serveripv4, sizeof(serveripv4), 0); if (flag & CLIENT_IPV6) { - status = pj_gethostip(pj_AF_INET6(), &hostip); - if (status != PJ_SUCCESS) - return -1031; + PJ_TEST_SUCCESS(pj_gethostip(pj_AF_INET6(), &hostip), + NULL, return -1031); pj_sockaddr_print(&hostip, serveripv6, sizeof(serveripv6), 0); } @@ -251,29 +252,35 @@ static int create_ice_strans(struct test_sess *test_sess, if ((ept->cfg.enable_stun & SRV)==SRV || (ept->cfg.enable_turn & SRV)==SRV) ice_cfg.resolver = test_sess->resolver; + /* Assertion in pj_stun_sock_start() if default_port is zero*/ +#define OR(val1,val2) (val1? val1 : val2) + if (flag & CLIENT_IPV4) { - set_stun_turn_cfg(ept, &ice_cfg, serveripv4, PJ_FALSE); + set_stun_turn_cfg(ept, &ice_cfg, serveripv4, + OR(test_sess->server1->stun_server_port, STUN_RANDOM_PORT), + OR(test_sess->server1->turn_server_port, TURN_RANDOM_PORT), + PJ_FALSE); } if (flag & CLIENT_IPV6) { - set_stun_turn_cfg(ept, &ice_cfg, serveripv6, PJ_TRUE); + set_stun_turn_cfg(ept, &ice_cfg, serveripv6, + OR(test_sess->server2->stun_server_port, STUN_RANDOM_PORT), + OR(test_sess->server2->turn_server_port, TURN_RANDOM_PORT), + PJ_TRUE); } +#undef OR /* Create ICE stream transport */ - status = pj_ice_strans_create(NULL, &ice_cfg, ept->cfg.comp_cnt, - (void*)ept, &ice_cb, - &ice); - if (status != PJ_SUCCESS) { - app_perror(INDENT "err: pj_ice_strans_create()", status); - return status; - } + PJ_TEST_SUCCESS(pj_ice_strans_create(NULL, &ice_cfg, ept->cfg.comp_cnt, + (void*)ept, &ice_cb, &ice), + NULL, return -1040); pj_create_unique_string(test_sess->pool, &ept->ufrag); pj_create_unique_string(test_sess->pool, &ept->pass); /* Looks alright */ *p_ice = ice; - return PJ_SUCCESS; + return 0; } /* Create test session */ @@ -289,10 +296,10 @@ static int create_sess(pj_stun_config *stun_cfg, pj_str_t ns_ip; pj_uint16_t ns_port; unsigned flags; - pj_status_t status = PJ_SUCCESS; + int rc; /* Create session structure */ - pool = pj_pool_create(mem, "testsess", 512, 512, NULL); + pool = pj_pool_create(stun_cfg->pf, "testsess", 512, 512, NULL); sess = PJ_POOL_ZALLOC_T(pool, struct test_sess); sess->pool = pool; sess->stun_cfg = stun_cfg; @@ -307,20 +314,17 @@ static int create_sess(pj_stun_config *stun_cfg, /* Create server */ flags = server_flag; if (flags & SERVER_IPV4) { - status = create_test_server(stun_cfg, (flags & ~SERVER_IPV6), - SRV_DOMAIN, &sess->server1); + PJ_TEST_SUCCESS(create_test_server(stun_cfg, (flags & ~SERVER_IPV6), + SRV_DOMAIN, &sess->server1), + NULL, { destroy_sess(sess, 500); return -10; }); } - if ((status == PJ_SUCCESS) && (flags & SERVER_IPV6)) { - status = create_test_server(stun_cfg, (flags & ~SERVER_IPV4), - SRV_DOMAIN, &sess->server2); + if (flags & SERVER_IPV6) { + PJ_TEST_SUCCESS(create_test_server(stun_cfg, (flags & ~SERVER_IPV4), + SRV_DOMAIN, &sess->server2), + NULL, { destroy_sess(sess, 500); return -11; }); } - if (status != PJ_SUCCESS) { - app_perror(INDENT "error: create_test_server()", status); - destroy_sess(sess, 500); - return -10; - } if (flags & SERVER_IPV4) { sess->server1->turn_respond_allocate = sess->server1->turn_respond_refresh = PJ_TRUE; @@ -337,36 +341,37 @@ static int create_sess(pj_stun_config *stun_cfg, (sess->caller.cfg.enable_stun & SRV)==SRV || (sess->caller.cfg.enable_turn & SRV)==SRV) { - status = pj_dns_resolver_create(mem, NULL, 0, stun_cfg->timer_heap, - stun_cfg->ioqueue, &sess->resolver); - if (status != PJ_SUCCESS) { - app_perror(INDENT "error: pj_dns_resolver_create()", status); - destroy_sess(sess, 500); - return -20; + PJ_TEST_SUCCESS(pj_dns_resolver_create(stun_cfg->pf, NULL, 0, + stun_cfg->timer_heap, + stun_cfg->ioqueue, + &sess->resolver), + NULL, { destroy_sess(sess, 500); return -20; }); + + if (flags & SERVER_IPV6) { + ns_ip = pj_str("::1"); + ns_port = sess->server2->dns_server_port; + } else { + ns_ip = pj_str("127.0.0.1"); + ns_port = sess->server1->dns_server_port; } - ns_ip = (flags & SERVER_IPV6)?pj_str("::1"):pj_str("127.0.0.1"); - ns_port = (pj_uint16_t)DNS_SERVER_PORT; - status = pj_dns_resolver_set_ns(sess->resolver, 1, &ns_ip, &ns_port); - if (status != PJ_SUCCESS) { - app_perror(INDENT "error: pj_dns_resolver_set_ns()", status); - destroy_sess(sess, 500); - return -21; - } + PJ_TEST_SUCCESS(pj_dns_resolver_set_ns(sess->resolver, 1, &ns_ip, + &ns_port), + NULL, { destroy_sess(sess, 500); return -21; }); } /* Create caller ICE stream transport */ - status = create_ice_strans(sess, &sess->caller, &sess->caller.ice); - if (status != PJ_SUCCESS) { + rc = create_ice_strans(sess, &sess->caller, &sess->caller.ice); + if (rc != 0) { destroy_sess(sess, 500); - return -30; + return rc; } /* Create callee ICE stream transport */ - status = create_ice_strans(sess, &sess->callee, &sess->callee.ice); - if (status != PJ_SUCCESS) { + rc = create_ice_strans(sess, &sess->callee, &sess->callee.ice); + if (rc != 0) { destroy_sess(sess, 500); - return -40; + return rc; } *p_sess = sess; @@ -502,22 +507,18 @@ static pj_status_t start_ice(struct ice_ept *ept, const struct ice_ept *remote) unsigned i; for (i=0; icfg.comp_cnt; ++i) { unsigned cnt = PJ_ARRAY_SIZE(rcand) - rcand_cnt; - status = pj_ice_strans_enum_cands(remote->ice, i+1, &cnt, rcand+rcand_cnt); - if (status != PJ_SUCCESS) { - app_perror(INDENT "err: pj_ice_strans_enum_cands()", status); - return status; - } + PJ_TEST_SUCCESS((status=pj_ice_strans_enum_cands(remote->ice, i+1, &cnt, + rcand+rcand_cnt)), + NULL, return status); rcand_cnt += cnt; } } - status = pj_ice_strans_start_ice(ept->ice, &remote->ufrag, &remote->pass, - rcand_cnt, rcand); - - if (status != ept->cfg.expected.start_status) { - app_perror(INDENT "err: pj_ice_strans_start_ice()", status); - return status; - } + PJ_TEST_EQ((status=pj_ice_strans_start_ice(ept->ice, &remote->ufrag, + &remote->pass, + rcand_cnt, rcand)), + ept->cfg.expected.start_status, NULL, + {return status==PJ_SUCCESS? PJ_EINVALIDOP : status; }); return status; } @@ -688,7 +689,7 @@ static int perform_test2(const char *title, if (rc != PJ_SUCCESS) { app_perror(INDENT "err: caller pj_ice_strans_init_ice()", rc); destroy_sess(sess, 500); - return -100; + return -106; } /* Init ICE on callee */ @@ -902,10 +903,10 @@ static int perform_test(const char *title, #define ROLE1 PJ_ICE_SESS_ROLE_CONTROLLED #define ROLE2 PJ_ICE_SESS_ROLE_CONTROLLING -int ice_test(void) +int ice_test(void *p) { - pj_pool_t *pool; - pj_stun_config stun_cfg; + unsigned test_id = (unsigned)(long)p; + app_sess_t app_sess; unsigned i; int rc; struct sess_cfg_t { @@ -913,7 +914,7 @@ int ice_test(void) unsigned server_flag; struct test_cfg ua1; struct test_cfg ua2; - } sess_cfg[] = + } sess_cfg[ICE_TEST_ARRAY_COUNT] = { /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */ { @@ -954,15 +955,14 @@ int ice_test(void) }, }; - pool = pj_pool_create(mem, NULL, 512, 512, NULL); - rc = create_stun_config(pool, &stun_cfg); - if (rc != PJ_SUCCESS) { - pj_pool_release(pool); + assert(test_id < ICE_TEST_START_ARRAY+PJ_ARRAY_SIZE(sess_cfg)); + + rc = create_stun_config(&app_sess); + if (rc != PJ_SUCCESS) return -7; - } /* Simple test first with host candidate */ - if (1) { + if (test_id==ICE_TEST_BASIC_HOST) { struct sess_cfg_t cfg = { "Basic with host candidates", @@ -972,7 +972,7 @@ int ice_test(void) {ROLE2, 1, YES, NO, NO, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}} }; - rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, + rc = perform_test(cfg.title, &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -980,14 +980,14 @@ int ice_test(void) cfg.ua1.comp_cnt = 2; cfg.ua2.comp_cnt = 2; rc = perform_test("Basic with host candidates, 2 components", - &stun_cfg, cfg.server_flag, + &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; } /* Simple test first with srflx candidate */ - if (1) { + if (test_id==ICE_TEST_BASIC_SRFLX) { struct sess_cfg_t cfg = { "Basic with srflx candidates", @@ -997,7 +997,7 @@ int ice_test(void) {ROLE2, 1, YES, YES, NO, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}} }; - rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, + rc = perform_test(cfg.title, &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -1006,14 +1006,14 @@ int ice_test(void) cfg.ua2.comp_cnt = 2; rc = perform_test("Basic with srflx candidates, 2 components", - &stun_cfg, cfg.server_flag, + &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; } /* Simple test with relay candidate */ - if (1) { + if (test_id==ICE_TEST_BASIC_RELAY) { struct sess_cfg_t cfg = { "Basic with relay candidates", @@ -1023,7 +1023,7 @@ int ice_test(void) {ROLE2, 1, NO, NO, YES, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}} }; - rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, + rc = perform_test(cfg.title, &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -1032,14 +1032,14 @@ int ice_test(void) cfg.ua2.comp_cnt = 2; rc = perform_test("Basic with relay candidates, 2 components", - &stun_cfg, cfg.server_flag, + &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; } /* Failure test with STUN resolution */ - if (1) { + if (test_id==ICE_TEST_STUN_RES_FAIL) { struct sess_cfg_t cfg = { "STUN resolution failure", @@ -1049,7 +1049,7 @@ int ice_test(void) {ROLE2, 2, NO, YES, NO, 0, 0, 0, 0, {PJ_SUCCESS, PJNATH_ESTUNTIMEDOUT, -1}} }; - rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, + rc = perform_test(cfg.title, &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -1058,14 +1058,14 @@ int ice_test(void) cfg.ua2.client_flag |= DEL_ON_ERR; rc = perform_test("STUN resolution failure with destroy on callback", - &stun_cfg, cfg.server_flag, + &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; } /* Failure test with TURN resolution */ - if (1) { + if (test_id==ICE_TEST_TURN_ALLOC_FAIL) { struct sess_cfg_t cfg = { "TURN allocation failure", @@ -1075,7 +1075,7 @@ int ice_test(void) {ROLE2, 2, NO, NO, YES, WRONG_TURN, 0, 0, 0, {PJ_SUCCESS, PJ_STATUS_FROM_STUN_CODE(401), -1}} }; - rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, + rc = perform_test(cfg.title, &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -1084,7 +1084,7 @@ int ice_test(void) cfg.ua2.client_flag |= DEL_ON_ERR; rc = perform_test("TURN allocation failure with destroy on callback", - &stun_cfg, cfg.server_flag, + &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -1092,7 +1092,7 @@ int ice_test(void) /* STUN failure, testing TURN deallocation */ - if (1) { + if (test_id==ICE_TEST_STUN_FAIL_TURN_DEALLOC) { struct sess_cfg_t cfg = { "STUN failure, testing TURN deallocation", @@ -1102,7 +1102,7 @@ int ice_test(void) {ROLE2, 1, YES, YES, YES, 0, 0, 0, 0, {PJ_SUCCESS, PJNATH_ESTUNTIMEDOUT, -1}} }; - rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, + rc = perform_test(cfg.title, &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -1111,15 +1111,15 @@ int ice_test(void) cfg.ua2.client_flag |= DEL_ON_ERR; rc = perform_test("STUN failure, testing TURN deallocation (cb)", - &stun_cfg, cfg.server_flag, + &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; } rc = 0; - /* Iterate each test item */ - for (i=0; i= ICE_TEST_START_ARRAY) { + i = test_id - ICE_TEST_START_ARRAY; struct sess_cfg_t *cfg = &sess_cfg[i]; unsigned delay[] = { 50, 2000 }; unsigned d; @@ -1166,7 +1166,7 @@ int ice_test(void) delay[d], k1, k2); cfg->ua2.comp_cnt = k2; - rc = perform_test(title, &stun_cfg, cfg->server_flag, + rc = perform_test(title, &app_sess.stun_cfg, cfg->server_flag, &cfg->ua1, &cfg->ua2); if (rc != 0) goto on_return; @@ -1177,8 +1177,7 @@ int ice_test(void) } on_return: - destroy_stun_config(&stun_cfg); - pj_pool_release(pool); + destroy_stun_config(&app_sess); return rc; } @@ -1239,22 +1238,19 @@ int ice_one_conc_test(pj_stun_config *stun_cfg, int err_quit) int ice_conc_test(void) { - const unsigned LOOP = 100; - pj_pool_t *pool; - pj_stun_config stun_cfg; + const unsigned LOOP = 20; + app_sess_t app_sess; unsigned i; int rc; - pool = pj_pool_create(mem, NULL, 512, 512, NULL); - rc = create_stun_config(pool, &stun_cfg); + rc = create_stun_config(&app_sess); if (rc != PJ_SUCCESS) { - pj_pool_release(pool); return -7; } for (i = 0; i < LOOP; i++) { PJ_LOG(3,(THIS_FILE, INDENT "Test %d of %d", i+1, LOOP)); - rc = ice_one_conc_test(&stun_cfg, PJ_TRUE); + rc = ice_one_conc_test(&app_sess.stun_cfg, PJ_TRUE); if (rc) break; } @@ -1263,8 +1259,7 @@ int ice_conc_test(void) goto on_return; on_return: - destroy_stun_config(&stun_cfg); - pj_pool_release(pool); + destroy_stun_config(&app_sess); return rc; } @@ -1531,8 +1526,7 @@ static int perform_trickle_test(const char *title, /* Simple trickle ICE test */ int trickle_ice_test(void) { - pj_pool_t *pool; - pj_stun_config stun_cfg; + app_sess_t app_sess; struct sess_param test_param; unsigned i; int rc; @@ -1569,11 +1563,8 @@ int trickle_ice_test(void) PJ_LOG(3,(THIS_FILE, "Trickle ICE")); pj_log_push_indent(); - pool = pj_pool_create(mem, NULL, 512, 512, NULL); - - rc = create_stun_config(pool, &stun_cfg); + rc = create_stun_config(&app_sess); if (rc != PJ_SUCCESS) { - pj_pool_release(pool); pj_log_pop_indent(); return -10; } @@ -1588,7 +1579,7 @@ int trickle_ice_test(void) cfg[i].ua1.comp_cnt = c1; cfg[i].ua2.comp_cnt = c2; rc = perform_trickle_test(cfg[i].title, - &stun_cfg, + &app_sess.stun_cfg, cfg[i].server_flag, &cfg[i].ua1, &cfg[i].ua2, @@ -1597,8 +1588,7 @@ int trickle_ice_test(void) } } - destroy_stun_config(&stun_cfg); - pj_pool_release(pool); + destroy_stun_config(&app_sess); pj_log_pop_indent(); return rc; diff --git a/pjnath/src/pjnath-test/main.c b/pjnath/src/pjnath-test/main.c index 9ecc43e948..7f184dd568 100644 --- a/pjnath/src/pjnath-test/main.c +++ b/pjnath/src/pjnath-test/main.c @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "test.h" +#include #if defined(PJ_SUNOS) && PJ_SUNOS!=0 @@ -33,19 +34,19 @@ static void init_signals() #elif (PJ_LINUX || PJ_DARWINOS) && defined(PJ_HAS_EXECINFO_H) && PJ_HAS_EXECINFO_H != 0 -#include +#include #include -#include -#include -#include +#include +#include +#include static void print_stack(int sig) { - void *array[16]; - size_t size; - - size = backtrace(array, 16); - fprintf(stderr, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, STDERR_FILENO); + void *array[16]; + size_t size; + + size = backtrace(array, 16); + fprintf(stderr, "Error: signal %d:\n", sig); + backtrace_symbols_fd(array, size, STDERR_FILENO); exit(1); } @@ -61,6 +62,21 @@ static void init_signals(void) #define boost() +static void usage() +{ + puts("Usage:"); + puts(" pjnath-test [OPTION] [test_to_run] [..]"); + puts(""); + puts("where OPTIONS:"); + puts(""); + puts(" -h, --help Show this help screen"); + + ut_usage(); + + puts(" -i Ask ENTER before quitting"); + puts(" -n Do not trap signals"); +} + int main(int argc, char *argv[]) { int rc; @@ -69,22 +85,25 @@ int main(int argc, char *argv[]) boost(); - while (argc > 1) { - char *arg = argv[--argc]; + if (pj_argparse_get_bool(&argc, argv, "-h") || + pj_argparse_get_bool(&argc, argv, "--help")) + { + usage(); + return 0; + } - if (*arg=='-' && *(arg+1)=='i') { - interractive = 1; + ut_app_init0(&test_app.ut_app); - } else if (*arg=='-' && *(arg+1)=='n') { - no_trap = 1; - } - } + interractive = pj_argparse_get_bool(&argc, argv, "-i"); + no_trap = pj_argparse_get_bool(&argc, argv, "-n"); + if (ut_parse_args(&test_app.ut_app, &argc, argv)) + return 1; if (!no_trap) { init_signals(); } - rc = test_main(); + rc = test_main(argc, argv); if (interractive) { char s[10]; diff --git a/pjnath/src/pjnath-test/server.c b/pjnath/src/pjnath-test/server.c index 5b6699df4b..4b5d72d41d 100644 --- a/pjnath/src/pjnath-test/server.c +++ b/pjnath/src/pjnath-test/server.c @@ -86,7 +86,7 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, { pj_pool_t *pool; test_server *test_srv; - pj_sockaddr hostip; + pj_sockaddr hostip, bound_addr; char strbuf[100]; pj_status_t status = PJ_EINVAL; pj_bool_t use_ipv6 = flags & SERVER_IPV6; @@ -105,7 +105,7 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, RETURN_ERROR(status); } - pool = pj_pool_create(mem, THIS_FILE, 512, 512, NULL); + pool = pj_pool_create(stun_cfg->pf, THIS_FILE, 512, 512, NULL); test_srv = (test_server*) PJ_POOL_ZALLOC_T(pool, test_server); test_srv->pool = pool; test_srv->flags = flags; @@ -118,14 +118,17 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, 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, - GET_AF(use_ipv6), DNS_SERVER_PORT, + status = pj_dns_server_create(stun_cfg->pf, test_srv->stun_cfg->ioqueue, + GET_AF(use_ipv6), 0, 0, &test_srv->dns_server); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); RETURN_ERROR(status); } + pj_dns_server_get_addr(test_srv->dns_server, &bound_addr); + test_srv->dns_server_port = pj_sockaddr_get_port(&bound_addr); + /* Add DNS A record for the domain, for fallback */ if (flags & CREATE_A_RECORD_FOR_DOMAIN) { pj_dns_parsed_rr rr; @@ -154,17 +157,18 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, stun_sock_cb.on_data_recvfrom = &stun_on_data_recvfrom; pj_sockaddr_init(GET_AF(use_ipv6), &bound_addr, - NULL, STUN_SERVER_PORT); + NULL, 0); status = pj_activesock_create_udp(pool, &bound_addr, NULL, test_srv->stun_cfg->ioqueue, &stun_sock_cb, test_srv, - &test_srv->stun_sock, NULL); + &test_srv->stun_sock, &bound_addr); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); RETURN_ERROR(status); } + test_srv->stun_server_port = pj_sockaddr_get_port(&bound_addr); status = pj_activesock_start_recvfrom(test_srv->stun_sock, pool, MAX_STUN_PKT, 0); if (status != PJ_SUCCESS) { @@ -187,7 +191,7 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, "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); + test_srv->stun_server_port, &target); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); res_name = target; @@ -208,7 +212,7 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, pj_sockaddr bound_addr; pj_turn_tp_type tp_type = get_turn_tp_type(flags); - pj_sockaddr_init(GET_AF(use_ipv6), &bound_addr, NULL, TURN_SERVER_PORT); + pj_sockaddr_init(GET_AF(use_ipv6), &bound_addr, NULL, 0); if (tp_type == PJ_TURN_TP_UDP) { pj_activesock_cb turn_sock_cb; @@ -219,18 +223,21 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, status = pj_activesock_create_udp(pool, &bound_addr, NULL, test_srv->stun_cfg->ioqueue, &turn_sock_cb, test_srv, - &test_srv->turn_sock, NULL); + &test_srv->turn_sock, + &bound_addr); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); RETURN_ERROR(status); } + test_srv->turn_server_port = pj_sockaddr_get_port(&bound_addr); status = pj_activesock_start_recvfrom(test_srv->turn_sock, pool, MAX_STUN_PKT, 0); } else if (tp_type == PJ_TURN_TP_TCP) { pj_sock_t sock_fd; pj_activesock_cb turn_sock_cb; + int name_len = sizeof(bound_addr); pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb)); turn_sock_cb.on_accept_complete2 = &turn_tcp_on_accept_complete; @@ -271,6 +278,11 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, status = pj_activesock_start_accept(test_srv->turn_sock, pool); + + if (pj_sock_getsockname(sock_fd, &bound_addr, &name_len)==PJ_SUCCESS) { + test_srv->turn_server_port = pj_sockaddr_get_port(&bound_addr); + } + } #if USE_TLS else if (tp_type == PJ_TURN_TP_TLS) { @@ -281,6 +293,7 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, pj_str_t cert_file = pj_str(CERT_FILE); pj_str_t privkey_file = pj_str(CERT_PRIVKEY_FILE); pj_str_t privkey_pass = pj_str(CERT_PRIVKEY_PASS); + pj_ssl_sock_info ssock_info; pj_ssl_sock_param_default(&ssl_param); ssl_param.cb.on_accept_complete2 = &turn_tls_on_accept_complete2; @@ -296,6 +309,7 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, pj_ssl_sock_close(ssock_serv); } +#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL) status = pj_ssl_cert_load_from_files(pool, &ca_file, &cert_file, &privkey_file, &privkey_pass, &cert); @@ -303,15 +317,32 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, if (ssock_serv) pj_ssl_sock_close(ssock_serv); } - status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert); if (status != PJ_SUCCESS) { if (ssock_serv) pj_ssl_sock_close(ssock_serv); } +#else + /* Schannel backend currently can only load certificates from + * OS cert store, here we don't load any cert so the SSL socket + * will create & use a self-signed cert. + */ + PJ_UNUSED_ARG(ca_file); + PJ_UNUSED_ARG(cert_file); + PJ_UNUSED_ARG(privkey_file); + PJ_UNUSED_ARG(privkey_pass); +#endif test_srv->ssl_srv_sock = ssock_serv; + status = pj_ssl_sock_start_accept(ssock_serv, pool, &bound_addr, pj_sockaddr_get_len(&bound_addr)); + + /* Can only get local address after start_accept() */ + if (pj_ssl_sock_get_info(ssock_serv, &ssock_info)==PJ_SUCCESS) { + test_srv->turn_server_port = + pj_sockaddr_get_port(&ssock_info.local_addr); + } + } #endif if (status != PJ_SUCCESS) { @@ -346,7 +377,7 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, "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); + test_srv->turn_server_port, &target); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); res_name = target; diff --git a/pjnath/src/pjnath-test/server.h b/pjnath/src/pjnath-test/server.h index bb9a9baeb3..4cfb52c944 100644 --- a/pjnath/src/pjnath-test/server.h +++ b/pjnath/src/pjnath-test/server.h @@ -23,10 +23,6 @@ #include #include -#define DNS_SERVER_PORT 55533 -#define STUN_SERVER_PORT 33478 -#define TURN_SERVER_PORT 33479 - #define TURN_USERNAME "auser" #define TURN_PASSWD "apass" @@ -81,10 +77,13 @@ struct test_server pj_ioqueue_op_key_t send_key; pj_dns_server *dns_server; + pj_uint16_t dns_server_port; pj_activesock_t *stun_sock; + pj_uint16_t stun_server_port; pj_activesock_t *turn_sock; + pj_uint16_t turn_server_port; pj_ssl_sock_t *ssl_srv_sock; diff --git a/pjnath/src/pjnath-test/stun_sock_test.c b/pjnath/src/pjnath-test/stun_sock_test.c index f44988aee3..8244e22ec0 100644 --- a/pjnath/src/pjnath-test/stun_sock_test.c +++ b/pjnath/src/pjnath-test/stun_sock_test.c @@ -832,53 +832,34 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) #define DO_TEST(expr) \ - capture_pjlib_state(&stun_cfg, &pjlib_state); \ + capture_pjlib_state(&app_sess.stun_cfg, &pjlib_state); \ ret = expr; \ if (ret != 0) goto on_return; \ - ret = check_pjlib_state(&stun_cfg, &pjlib_state); \ + ret = check_pjlib_state(&app_sess.stun_cfg, &pjlib_state); \ if (ret != 0) goto on_return; int stun_sock_test(void) { struct pjlib_state pjlib_state; - pj_stun_config stun_cfg; - pj_ioqueue_t *ioqueue = NULL; - pj_timer_heap_t *timer_heap = NULL; - pj_pool_t *pool = NULL; + app_sess_t app_sess; pj_status_t status; int ret = 0; - pool = pj_pool_create(mem, NULL, 512, 512, NULL); + status = create_stun_config(&app_sess); + if (status) + return -11; - status = pj_ioqueue_create(pool, 12, &ioqueue); - if (status != PJ_SUCCESS) { - app_perror(" pj_ioqueue_create()", status); - ret = -4; - goto on_return; - } - - status = pj_timer_heap_create(pool, 100, &timer_heap); - if (status != PJ_SUCCESS) { - app_perror(" pj_timer_heap_create()", status); - ret = -8; - goto on_return; - } - - pj_stun_config_init(&stun_cfg, mem, 0, ioqueue, timer_heap); - - DO_TEST(timeout_test(&stun_cfg, PJ_FALSE, USE_IPV6)); - DO_TEST(timeout_test(&stun_cfg, PJ_TRUE, USE_IPV6)); + DO_TEST(timeout_test(&app_sess.stun_cfg, PJ_FALSE, USE_IPV6)); + DO_TEST(timeout_test(&app_sess.stun_cfg, PJ_TRUE, USE_IPV6)); - DO_TEST(missing_attr_test(&stun_cfg, PJ_FALSE, USE_IPV6)); - DO_TEST(missing_attr_test(&stun_cfg, PJ_TRUE, USE_IPV6)); + DO_TEST(missing_attr_test(&app_sess.stun_cfg, PJ_FALSE, USE_IPV6)); + DO_TEST(missing_attr_test(&app_sess.stun_cfg, PJ_TRUE, USE_IPV6)); - DO_TEST(keep_alive_test(&stun_cfg, USE_IPV6)); + DO_TEST(keep_alive_test(&app_sess.stun_cfg, USE_IPV6)); on_return: - if (timer_heap) pj_timer_heap_destroy(timer_heap); - if (ioqueue) pj_ioqueue_destroy(ioqueue); - if (pool) pj_pool_release(pool); + destroy_stun_config(&app_sess); return ret; } diff --git a/pjnath/src/pjnath-test/test.c b/pjnath/src/pjnath-test/test.c index 8626ac80d9..41130524fb 100644 --- a/pjnath/src/pjnath-test/test.c +++ b/pjnath/src/pjnath-test/test.c @@ -20,6 +20,8 @@ #include #include +#define THIS_FILE "test.c" + void app_perror_dbg(const char *msg, pj_status_t rc, const char *file, int line) { @@ -46,44 +48,63 @@ void app_set_sock_nb(pj_sock_t sock) #endif } -pj_status_t create_stun_config(pj_pool_t *pool, pj_stun_config *stun_cfg) +pj_status_t create_stun_config(app_sess_t *app_sess) { - pj_ioqueue_t *ioqueue; - pj_timer_heap_t *timer_heap; - pj_lock_t *lock; + pj_ioqueue_t *ioqueue = NULL; + pj_timer_heap_t *timer_heap = NULL; + pj_lock_t *lock = NULL; pj_status_t status; - status = pj_ioqueue_create(pool, 64, &ioqueue); - if (status != PJ_SUCCESS) { - app_perror(" pj_ioqueue_create()", status); - return status; - } + pj_bzero(app_sess, sizeof(*app_sess)); + pj_caching_pool_init(&app_sess->cp, + &pj_pool_factory_default_policy, 0 ); + app_sess->pool = pj_pool_create(&app_sess->cp.factory, NULL, + 512, 512, NULL); + PJ_TEST_NOT_NULL(app_sess->pool, NULL, + { status=PJ_ENOMEM; goto on_error;}); - status = pj_timer_heap_create(pool, 256, &timer_heap); - if (status != PJ_SUCCESS) { - app_perror(" pj_timer_heap_create()", status); - pj_ioqueue_destroy(ioqueue); - return status; - } + PJ_TEST_SUCCESS(pj_ioqueue_create(app_sess->pool, 64, &ioqueue), NULL, + goto on_error); + PJ_TEST_SUCCESS(pj_timer_heap_create(app_sess->pool, 256, &timer_heap), + NULL, goto on_error); - pj_lock_create_recursive_mutex(pool, NULL, &lock); + pj_lock_create_recursive_mutex(app_sess->pool, NULL, &lock); pj_timer_heap_set_lock(timer_heap, lock, PJ_TRUE); + lock = NULL; - pj_stun_config_init(stun_cfg, mem, 0, ioqueue, timer_heap); + pj_stun_config_init(&app_sess->stun_cfg, app_sess->pool->factory, 0, + ioqueue, timer_heap); return PJ_SUCCESS; + +on_error: + if (ioqueue) + pj_ioqueue_destroy(ioqueue); + if (timer_heap) + pj_timer_heap_destroy(timer_heap); + if (lock) + pj_lock_destroy(lock); + if (app_sess->pool) + pj_pool_release(app_sess->pool); + pj_caching_pool_destroy(&app_sess->cp); + return status; } -void destroy_stun_config(pj_stun_config *stun_cfg) +void destroy_stun_config(app_sess_t *app_sess) { - if (stun_cfg->timer_heap) { - pj_timer_heap_destroy(stun_cfg->timer_heap); - stun_cfg->timer_heap = NULL; + if (app_sess->stun_cfg.timer_heap) { + pj_timer_heap_destroy(app_sess->stun_cfg.timer_heap); + app_sess->stun_cfg.timer_heap = NULL; + } + if (app_sess->stun_cfg.ioqueue) { + pj_ioqueue_destroy(app_sess->stun_cfg.ioqueue); + app_sess->stun_cfg.ioqueue = NULL; } - if (stun_cfg->ioqueue) { - pj_ioqueue_destroy(stun_cfg->ioqueue); - stun_cfg->ioqueue = NULL; + if (app_sess->pool) { + pj_pool_release(app_sess->pool); + app_sess->pool = NULL; } + pj_caching_pool_destroy(&app_sess->cp); } void poll_events(pj_stun_config *stun_cfg, unsigned msec, @@ -138,18 +159,18 @@ int check_pjlib_state(pj_stun_config *cfg, capture_pjlib_state(cfg, ¤t_state); if (current_state.timer_cnt > initial_st->timer_cnt) { - PJ_LOG(3,("", " error: possibly leaking timer")); rc |= ERR_TIMER_LEAK; #if PJ_TIMER_DEBUG pj_timer_heap_dump(cfg->timer_heap); #endif + PJ_LOG(3,("", " error: possibly leaking timer")); } if (current_state.pool_used_cnt > initial_st->pool_used_cnt) { - PJ_LOG(3,("", " error: possibly leaking memory")); PJ_LOG(3,("", " dumping memory pool:")); - pj_pool_factory_dump(mem, PJ_TRUE); + pj_pool_factory_dump(cfg->pf, PJ_TRUE); + PJ_LOG(3,("", " error: possibly leaking memory")); rc |= ERR_MEMORY_LEAK; } @@ -157,17 +178,8 @@ int check_pjlib_state(pj_stun_config *cfg, } -#define DO_TEST(test) do { \ - PJ_LOG(3, ("test", "Running %s...", #test)); \ - rc = test; \ - PJ_LOG(3, ("test", \ - "%s(%d)", \ - (char*)(rc ? "..ERROR" : "..success"), rc)); \ - if (rc!=0) goto on_return; \ - } while (0) - - pj_pool_factory *mem; +struct test_app_t test_app; int param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | PJ_LOG_HAS_SENDER | PJ_LOG_HAS_MICRO_SEC; @@ -184,10 +196,10 @@ static void test_log_func(int level, const char *data, int len) orig_log_func(level, data, len); } -static int test_inner(void) +static int test_inner(int argc, char *argv[]) { pj_caching_pool caching_pool; - int rc = 0; + int i, rc = 0; mem = &caching_pool.factory; @@ -202,55 +214,73 @@ static int test_inner(void) pj_log_set_log_func(&test_log_func); #endif - rc = pj_init(); - if (rc != 0) { - app_perror("pj_init() error!!", rc); - goto on_return; - } + PJ_TEST_SUCCESS(pj_init(), NULL, + {if (log_file) fclose(log_file); return 1; }); - pj_dump_config(); + if (test_app.ut_app.prm_config) + pj_dump_config(); pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 ); - pjlib_util_init(); - pjnath_init(); + PJ_TEST_SUCCESS(pjlib_util_init(), NULL, {rc=2; goto on_return;}); + PJ_TEST_SUCCESS(pjnath_init(), NULL, {rc=3; goto on_return;}); + PJ_TEST_SUCCESS(ut_app_init1(&test_app.ut_app, mem), + NULL, {rc=4; goto on_return;}); #if INCLUDE_STUN_TEST - DO_TEST(stun_test()); - DO_TEST(sess_auth_test()); + UT_ADD_TEST(&test_app.ut_app, stun_test, 0); + UT_ADD_TEST(&test_app.ut_app, sess_auth_test, 0); #endif -#if INCLUDE_ICE_TEST - DO_TEST(ice_test()); +#if INCLUDE_STUN_SOCK_TEST + UT_ADD_TEST(&test_app.ut_app, stun_sock_test, 0); #endif -#if INCLUDE_TRICKLE_ICE_TEST - DO_TEST(trickle_ice_test()); +#if INCLUDE_ICE_TEST + for (i=0; itest_srv->dns_server_port; status = pj_dns_resolver_set_ns(sess->resolver, 1, &dns_srv, &dns_srv_port); if (status != PJ_SUCCESS) { @@ -178,13 +179,15 @@ static int create_test_session(pj_stun_config *stun_cfg, if (cfg->client.enable_dns_srv) { /* Use DNS SRV to resolve server, may fallback to DNS A */ pj_str_t domain = pj_str(SRV_DOMAIN); - status = pj_turn_sock_alloc(sess->turn_sock, &domain, TURN_SERVER_PORT, + status = pj_turn_sock_alloc(sess->turn_sock, &domain, + sess->test_srv->turn_server_port, sess->resolver, &cred, &alloc_param); } else { /* Explicitly specify server address */ pj_str_t host = use_ipv6?pj_str("::1") : pj_str("127.0.0.1"); - status = pj_turn_sock_alloc(sess->turn_sock, &host, TURN_SERVER_PORT, + status = pj_turn_sock_alloc(sess->turn_sock, &host, + sess->test_srv->turn_server_port, NULL, &cred, &alloc_param); } @@ -531,50 +534,50 @@ static int destroy_test(pj_stun_config *stun_cfg, ///////////////////////////////////////////////////////////////////// -int turn_sock_test(void) +int turn_sock_test(void *p) { - pj_pool_t *pool; - pj_stun_config stun_cfg; - int n, i, rc = 0; + unsigned n = (int)(long)p; + app_sess_t app_sess; + pj_turn_tp_type tp_type = PJ_TURN_TP_UDP; + int i, rc = 0; + + if ((n == 2) && !USE_TLS) + return 0; + + switch (n) { + case 0: + tp_type = PJ_TURN_TP_UDP; + break; + case 1: + tp_type = PJ_TURN_TP_TCP; + break; + case 2: + tp_type = PJ_TURN_TP_TLS; + break; + default: + PJ_TEST_LTE(n, 2, NULL, return -1); + } - pool = pj_pool_create(mem, "turntest", 512, 512, NULL); - rc = create_stun_config(pool, &stun_cfg); + rc = create_stun_config(&app_sess); if (rc != PJ_SUCCESS) { - pj_pool_release(pool); return -2; } - for (n = 0; n <= 2; ++n) { - pj_turn_tp_type tp_type = PJ_TURN_TP_UDP; - - if ((n == 2) && !USE_TLS) - break; - - switch (n) { - case 1: - tp_type = PJ_TURN_TP_TCP; - break; - case 2: - tp_type = PJ_TURN_TP_TLS; - } - - rc = state_progression_test(&stun_cfg, USE_IPV6, tp_type); - if (rc != 0) - goto on_return; + rc = state_progression_test(&app_sess.stun_cfg, USE_IPV6, tp_type); + if (rc != 0) + goto on_return; - for (i=0; i<=1; ++i) { - int j; - for (j=0; j<=1; ++j) { - rc = destroy_test(&stun_cfg, i, j, USE_IPV6, tp_type); - if (rc != 0) - goto on_return; - } + for (i=0; i<=1; ++i) { + int j; + for (j=0; j<=1; ++j) { + rc = destroy_test(&app_sess.stun_cfg, i, j, USE_IPV6, tp_type); + if (rc != 0) + goto on_return; } } on_return: - destroy_stun_config(&stun_cfg); - pj_pool_release(pool); + destroy_stun_config(&app_sess); return rc; } diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h index 75fcfad034..14c2e5c522 100644 --- a/pjsip/include/pjsip/sip_transport.h +++ b/pjsip/include/pjsip/sip_transport.h @@ -1302,6 +1302,19 @@ PJ_DECL(pj_status_t) pjsip_tpmgr_find_local_addr2(pjsip_tpmgr *tpmgr, PJ_DECL(unsigned) pjsip_tpmgr_get_transport_count(pjsip_tpmgr *mgr); +/** + * Return number of transports of the specified type currently registered to + * the transport manager. + * + * @param mgr The transport manager. + * @param type Transport type (can be from pjsip_transport_type_e enum) + * + * @return Number of transports. + */ +PJ_DECL(unsigned) pjsip_tpmgr_get_transport_count_by_type(pjsip_tpmgr *mgr, + int type); + + /** * Destroy a transport manager. Normally application doesn't need to call * this function directly, since a transport manager will be created and diff --git a/pjsip/src/pjsip/sip_resolve.c b/pjsip/src/pjsip/sip_resolve.c index bd7942415f..b58e67ac6c 100644 --- a/pjsip/src/pjsip/sip_resolve.c +++ b/pjsip/src/pjsip/sip_resolve.c @@ -445,7 +445,6 @@ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, else { pj_assert(!"Unknown transport type"); query->naptr[0].res_type = pj_str("_sip._udp."); - } } else { diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index c5885f3056..7ce617d4c7 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -1907,6 +1907,12 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr, * manager. */ PJ_DEF(unsigned) pjsip_tpmgr_get_transport_count(pjsip_tpmgr *mgr) +{ + return pjsip_tpmgr_get_transport_count_by_type(mgr, -1); +} + +PJ_DEF(unsigned) pjsip_tpmgr_get_transport_count_by_type(pjsip_tpmgr *mgr, + int type) { pj_hash_iterator_t itr_val; pj_hash_iterator_t *itr; @@ -1917,7 +1923,15 @@ PJ_DEF(unsigned) pjsip_tpmgr_get_transport_count(pjsip_tpmgr *mgr) itr = pj_hash_first(mgr->table, &itr_val); while (itr) { transport *tp_entry = (transport *)pj_hash_this(mgr->table, itr); - nr_of_transports += (int)pj_list_size(tp_entry); + if (type<0) { + nr_of_transports += (int)pj_list_size(tp_entry); + } else { + transport *node = tp_entry->next; + for (; node!=tp_entry; node=node->next) { + if (tp_entry->tp->key.type==type) + ++nr_of_transports; + } + } itr = pj_hash_next(mgr->table, itr); } diff --git a/pjsip/src/test/dns_test.c b/pjsip/src/test/dns_test.c index 29dc762223..86ed8cd53e 100644 --- a/pjsip/src/test/dns_test.c +++ b/pjsip/src/test/dns_test.c @@ -331,7 +331,11 @@ static int test_resolve(const char *title, int port, pjsip_server_addresses *ref) { + enum { + TIMEOUT_SECS = 60 + }; pjsip_host_info dest; + pj_time_val timeout; struct result result; PJ_LOG(3,(THIS_FILE, " test_resolve(): %s", title)); @@ -343,14 +347,22 @@ static int test_resolve(const char *title, result.status = 0x12345678; + pj_gettimeofday(&timeout); + timeout.sec += TIMEOUT_SECS; + pjsip_endpt_resolve(endpt, pool, &dest, &result, &cb); while (result.status == 0x12345678) { int i = 0; pj_time_val timeout = { 1, 0 }; + pj_time_val now; + pjsip_endpt_handle_events(endpt, &timeout); if (i == 1) pj_dns_resolver_dump(pjsip_endpt_get_resolver(endpt), PJ_TRUE); + + pj_gettimeofday(&now); + PJ_TEST_TRUE(PJ_TIME_VAL_LT(now, timeout), NULL, return 12345678); } if (result.status != PJ_SUCCESS) { diff --git a/pjsip/src/test/inv_offer_answer_test.c b/pjsip/src/test/inv_offer_answer_test.c index 2fc0d22c7e..80b9ae552c 100644 --- a/pjsip/src/test/inv_offer_answer_test.c +++ b/pjsip/src/test/inv_offer_answer_test.c @@ -23,9 +23,9 @@ #include #define THIS_FILE "inv_offer_answer_test.c" -#define PORT 5068 -#define CONTACT "sip:127.0.0.1:5068" -#define TRACE_(x) PJ_LOG(3,x) +#define PORT 50068 +#define CONTACT "sip:inv_offer_answer_test@127.0.0.1:50068" +#define TRACE_(x) //PJ_LOG(3,x) static struct oa_sdp_t { @@ -277,6 +277,7 @@ static void on_state_changed(pjsip_inv_session *inv, pjsip_event *e) const char *who = NULL; PJ_UNUSED_ARG(e); + PJ_UNUSED_ARG(who); if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { TRACE_((THIS_FILE, " %s call disconnected", @@ -314,15 +315,17 @@ static pj_bool_t on_rx_request(pjsip_rx_data *rdata) pjmedia_sdp_session *sdp = NULL; pj_str_t uri; pjsip_tx_data *tdata; - pj_status_t status; + + if (!is_user_equal(rdata->msg_info.from, "inv_offer_answer_test")) + return PJ_FALSE; /* * Create UAS */ uri = pj_str(CONTACT); - status = pjsip_dlg_create_uas_and_inc_lock(pjsip_ua_instance(), rdata, - &uri, &dlg); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pjsip_dlg_create_uas_and_inc_lock(pjsip_ua_instance(), rdata, + &uri, &dlg), + NULL, { pj_assert(0); return PJ_FALSE; }); if (inv_test.param.oa[0] == OFFERER_UAC) sdp = create_sdp(rdata->tp_info.pool, oa_sdp[0].answer); @@ -331,8 +334,10 @@ static pj_bool_t on_rx_request(pjsip_rx_data *rdata) else pj_assert(!"Invalid offerer type"); - status = pjsip_inv_create_uas(dlg, rdata, sdp, inv_test.param.inv_option, &inv_test.uas); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pjsip_inv_create_uas(dlg, rdata, sdp, + inv_test.param.inv_option, + &inv_test.uas), + NULL, { pj_assert(0); return PJ_FALSE; }); pjsip_dlg_dec_lock(dlg); TRACE_((THIS_FILE, " Sending 183 with SDP")); @@ -340,23 +345,23 @@ static pj_bool_t on_rx_request(pjsip_rx_data *rdata) /* * Answer with 183 */ - status = pjsip_inv_initial_answer(inv_test.uas, rdata, 183, NULL, - NULL, &tdata); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pjsip_inv_initial_answer(inv_test.uas, rdata, 183, + NULL, NULL, &tdata), + NULL, { pj_assert(0); return PJ_FALSE; }); /* Use multipart body, if configured */ if (sdp && inv_test.param.multipart_body) { - status = pjsip_create_multipart_sdp_body( + PJ_TEST_SUCCESS(pjsip_create_multipart_sdp_body( tdata->pool, pjmedia_sdp_session_clone(tdata->pool, sdp), - &tdata->msg->body); + &tdata->msg->body), + NULL, { pj_assert(0); return PJ_FALSE; }); } - pj_assert(status == PJ_SUCCESS); - status = pjsip_inv_send_msg(inv_test.uas, tdata); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pjsip_inv_send_msg(inv_test.uas, tdata), + NULL, { pj_assert(0); return PJ_FALSE; }); - return (status == PJ_SUCCESS); + return PJ_TRUE; } return PJ_FALSE; @@ -466,8 +471,8 @@ static int perform_test(inv_test_param_t *param) } PJ_ASSERT_RETURN(status==PJ_SUCCESS, -40); - status = pjsip_inv_send_msg(inv_test.uac, tdata); - PJ_ASSERT_RETURN(status==PJ_SUCCESS, -50); + PJ_TEST_SUCCESS(pjsip_inv_send_msg(inv_test.uac, tdata), + NULL, PJ_ASSERT_RETURN(0, -50)); /* * Wait until test completes @@ -498,8 +503,8 @@ static int perform_test(inv_test_param_t *param) pj_assert(status == PJ_SUCCESS); if (tdata) { - status = pjsip_inv_send_msg(inv_test.uas, tdata); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pjsip_inv_send_msg(inv_test.uas, tdata), + NULL, pj_assert(0)); } flush_events(500); @@ -513,6 +518,9 @@ static pj_bool_t log_on_rx_msg(pjsip_rx_data *rdata) pjsip_msg *msg = rdata->msg_info.msg; char info[80]; + if (!is_user_equal(rdata->msg_info.from, "inv_offer_answer_test")) + return PJ_FALSE; + if (msg->type == PJSIP_REQUEST_MSG) pj_ansi_snprintf(info, sizeof(info), "%.*s", (int)msg->line.req.method.name.slen, @@ -764,12 +772,10 @@ int inv_offer_answer_test(void) { pj_sockaddr_in addr; pjsip_transport *tp; - pj_status_t status; pj_sockaddr_in_init(&addr, NULL, PORT); - status = pjsip_udp_transport_start(endpt, &addr, NULL, 1, &tp); - pj_assert(status == PJ_SUCCESS); - PJ_UNUSED_ARG(status); + PJ_TEST_SUCCESS(pjsip_udp_transport_start(endpt, &addr, NULL, 1, &tp), + NULL, return -5); } /* Do tests */ diff --git a/pjsip/src/test/main.c b/pjsip/src/test/main.c index 18dc2e266c..cf17b5e177 100644 --- a/pjsip/src/test/main.c +++ b/pjsip/src/test/main.c @@ -20,19 +20,39 @@ #include #include #include +#include extern const char *system_name; +static void warn(void) +{ + puts("********************************************************************"); + puts("** W A R N I N G **"); + puts("********************************************************************"); + puts("** Due to centralized event processing in PJSIP, events may be **"); + puts("** read by different thread than the test's thread. This may **"); + puts("** cause logs to be saved by the wrong test when multithreaded **"); + puts("** testing is used. The test results are correct, but the log **"); + puts("** may not be accurate. **"); + puts("** For debugging with correct logging, use \"-w 0 --log-no-cache\" **"); + puts("********************************************************************"); +} + static void usage(void) { - puts("Usage: test-pjsip"); - puts("Options:"); - puts(" -i,--interractive Key input at the end."); - puts(" -h,--help Show this screen"); - puts(" -l,--log-level N Set log level (0-6)"); - puts(" -n,--no-trap Let signals be handled by the OS"); - puts(" -t,--tests testname,... Comma separated list of test to run"); - list_tests(); + puts("Usage:"); + puts(" pjsip-test [OPTIONS] [test_to_run] [test to run] [..]"); + puts(""); + puts("where OPTIONS:"); + puts(""); + puts(" -h,--help Show this screen"); + + ut_usage(); + + puts(" -i,--interractive Key input at the end."); + puts(" -n,--no-trap Let signals be handled by the OS"); + puts(" --log-level N Set log level (0-6)"); + puts(" -s,--system NAME Set system name to NAME"); } #if (PJ_LINUX || PJ_DARWINOS) && defined(PJ_HAS_EXECINFO_H) && PJ_HAS_EXECINFO_H != 0 @@ -67,68 +87,41 @@ int main(int argc, char *argv[]) { int interractive = 0; int retval; - char **opt_arg; int no_trap = 0; - char *testlist = NULL; - - PJ_UNUSED_ARG(argc); - - /* Parse arguments. */ - opt_arg = argv+1; - while (*opt_arg) { - if (strcmp(*opt_arg, "-i") == 0 || - strcmp(*opt_arg, "--interractive") == 0) - { - interractive = 1; - } else if (strcmp(*opt_arg, "-n") == 0 || - strcmp(*opt_arg, "--no-trap") == 0) - { - no_trap = 1; - } else if (strcmp(*opt_arg, "-h") == 0 || - strcmp(*opt_arg, "--help") == 0) - { - usage(); - return 1; - } else if (strcmp(*opt_arg, "-l") == 0 || - strcmp(*opt_arg, "--log-level") == 0) - { - ++opt_arg; - if (!*opt_arg) { - usage(); - return 1; - } - log_level = atoi(*opt_arg); - } else if (strcmp(*opt_arg, "-s") == 0 || - strcmp(*opt_arg, "--system") == 0) - { - ++opt_arg; - if (!*opt_arg) { - usage(); - return 1; - } - system_name = *opt_arg; - } else if (strcmp(*opt_arg, "-t") == 0 || - strcmp(*opt_arg, "--tests") == 0) - { - ++opt_arg; - if (!*opt_arg) { - usage(); - return 1; - } - testlist = *opt_arg; - } else { - usage(); - return 1; - } - - ++opt_arg; + + warn(); + + if (pj_argparse_get_bool(&argc, argv, "-h") || + pj_argparse_get_bool(&argc, argv, "--help")) + { + usage(); + return 0; } + ut_app_init0(&test_app.ut_app); + + interractive = pj_argparse_get_bool(&argc, argv, "-i"); + no_trap = pj_argparse_get_bool(&argc, argv, "-n"); + if (pj_argparse_get_str(&argc, argv, "-s", (char**)&system_name) || + pj_argparse_get_str(&argc, argv, "--system", (char**)&system_name)) + { + usage(); + return 1; + } + + if (pj_argparse_get_int(&argc, argv, "--log-level", &log_level)) { + usage(); + return 1; + } + + if (ut_parse_args(&test_app.ut_app, &argc, argv)) + return 1; + if (!no_trap) { init_signals(); } - retval = test_main(testlist); + retval = test_main(argc, argv); if (interractive) { char s[10]; diff --git a/pjsip/src/test/regc_test.c b/pjsip/src/test/regc_test.c index 9708208bd2..5f978f8acf 100644 --- a/pjsip/src/test/regc_test.c +++ b/pjsip/src/test/regc_test.c @@ -39,14 +39,14 @@ static struct NULL, NULL, /* prev, next. */ { "mod-send", 8 }, /* Name. */ -1, /* Id */ - PJSIP_MOD_PRIORITY_TRANSPORT_LAYER, /* Priority */ + PJSIP_MOD_PRIORITY_TRANSPORT_LAYER, /* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ NULL, /* unload() */ NULL, /* on_rx_request() */ NULL, /* on_rx_response() */ - &mod_send_on_tx_request, /* on_tx_request. */ + &mod_send_on_tx_request, /* on_tx_request. */ NULL, /* on_tx_response() */ NULL, /* on_tsx_state() */ }, @@ -57,7 +57,12 @@ static struct static pj_status_t mod_send_on_tx_request(pjsip_tx_data *tdata) { - PJ_UNUSED_ARG(tdata); + const pjsip_from_hdr *from_hdr = (const pjsip_from_hdr*) + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL); + + if ((tdata->msg->line.req.method.id != PJSIP_REGISTER_METHOD) || + !is_user_equal(from_hdr, "regc-test")) + return PJ_FALSE; if (++send_mod.count > send_mod.count_before_reject) return PJ_ECANCELLED; @@ -120,7 +125,8 @@ static pj_bool_t regs_rx_request(pjsip_rx_data *rdata) int code; pj_status_t status; - if (msg->line.req.method.id != PJSIP_REGISTER_METHOD) + if ((rdata->msg_info.msg->line.req.method.id != PJSIP_REGISTER_METHOD) || + !is_user_equal(rdata->msg_info.from, "regc-test")) return PJ_FALSE; if (!registrar.cfg.respond) @@ -322,6 +328,9 @@ static int do_test(const char *title, pjsip_regc_destroy(regc); return -120; } + /* Add additional ref counter to prevent premature destroy */ + pjsip_tx_data_add_ref(tdata); + status = pjsip_regc_send(regc, tdata); /* That's it, wait until the callback is sent */ @@ -332,6 +341,7 @@ static int do_test(const char *title, if (!client_result.done) { PJ_LOG(3,(THIS_FILE, " error: test has timed out")); pjsip_regc_destroy(regc); + pjsip_tx_data_dec_ref(tdata); return -200; } @@ -346,6 +356,7 @@ static int do_test(const char *title, if (client_result.error != client_cfg->error) { PJ_LOG(3,(THIS_FILE, " error: expecting err=%d, got err=%d", client_cfg->error, client_result.error)); + pjsip_tx_data_dec_ref(tdata); return -210; } if (client_result.code != client_cfg->code && @@ -354,31 +365,37 @@ static int do_test(const char *title, { PJ_LOG(3,(THIS_FILE, " error: expecting code=%d, got code=%d", client_cfg->code, client_result.code)); + pjsip_tx_data_dec_ref(tdata); return -220; } if (client_result.expiration != client_cfg->expiration) { PJ_LOG(3,(THIS_FILE, " error: expecting expiration=%d, got expiration=%d", client_cfg->expiration, client_result.expiration)); + pjsip_tx_data_dec_ref(tdata); return -240; } if (client_result.contact_cnt != client_cfg->contact_cnt) { PJ_LOG(3,(THIS_FILE, " error: expecting contact_cnt=%d, got contact_cnt=%d", client_cfg->contact_cnt, client_result.contact_cnt)); + pjsip_tx_data_dec_ref(tdata); return -250; } if (client_result.have_reg != client_cfg->have_reg) { PJ_LOG(3,(THIS_FILE, " error: expecting have_reg=%d, got have_reg=%d", client_cfg->have_reg, client_result.have_reg)); + pjsip_tx_data_dec_ref(tdata); return -260; } if (client_result.have_reg && client_result.interval != client_result.expiration) { PJ_LOG(3,(THIS_FILE, " error: interval (%d) is different than expiration (%d)", client_result.interval, client_result.expiration)); + pjsip_tx_data_dec_ref(tdata); return -270; } if (client_result.interval > 0 && client_result.next_reg < 1) { PJ_LOG(3,(THIS_FILE, " error: next_reg=%d, expecting positive number because interval is %d", client_result.next_reg, client_result.interval)); + pjsip_tx_data_dec_ref(tdata); return -280; } @@ -387,6 +404,7 @@ static int do_test(const char *title, *p_regc = regc; } + pjsip_tx_data_dec_ref(tdata); return 0; } diff --git a/pjsip/src/test/test.c b/pjsip/src/test/test.c index cda6d3c7aa..a2f63f7ffc 100644 --- a/pjsip/src/test/test.c +++ b/pjsip/src/test/test.c @@ -25,29 +25,6 @@ #define THIS_FILE "test.c" -#define DO_TEST(test) do { \ - PJ_LOG(3, (THIS_FILE, "Running %s...", #test)); \ - rc = test; \ - PJ_LOG(3, (THIS_FILE, \ - "%s(%d)", \ - (rc ? "..ERROR" : "..success"), rc)); \ - if (rc!=0) goto on_return; \ - } while (0) - -#define DO_TSX_TEST(test, param) \ - do { \ - PJ_LOG(3, (THIS_FILE, "Running %s(%s)...", #test, (param)->tp_type)); \ - rc = test(param); \ - PJ_LOG(3, (THIS_FILE, \ - "%s(%d)", \ - (rc ? "..ERROR" : "..success"), rc)); \ - if (rc!=0) goto on_return; \ - } while (0) - -#if defined(_MSC_VER) || defined(__MINGW32__) -# define strtok_r strtok_s -#endif - pjsip_endpoint *endpt; pj_caching_pool caching_pool; int log_level = 3; @@ -57,85 +34,8 @@ int param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | PJ_LOG_HAS_SENDER | static pj_oshandle_t fd_report; const char *system_name = "Unknown"; static char buf[1024]; - -static struct { - const char *name; - int run_test; -} test_list[] = { - { "uri", 0}, - { "msg", 0}, - { "multipart", 0}, - { "txdata", 0}, - { "tsx_bench", 0}, - { "udp", 0}, - { "loop", 0}, - { "tcp", 0}, - { "resolve", 0}, - { "tsx", 0}, - { "tsx_destroy", 0}, - { "inv_oa", 0}, - { "regc", 0}, -}; -enum tests_to_run { - include_uri_test = 0, - include_msg_test, - include_multipart_test, - include_txdata_test, - include_tsx_bench, - include_udp_test, - include_loop_test, - include_tcp_test, - include_resolve_test, - include_tsx_test, - include_tsx_destroy_test, - include_inv_oa_test, - include_regc_test, -}; -static int run_all_tests = 1; - -static pj_status_t select_tests(char *testlist) -{ - char *token; - char *saveptr; - int maxtok = PJ_ARRAY_SIZE(test_list); - int j; - - if (!testlist) { - return PJ_SUCCESS; - } - run_all_tests = 0; - - for (token = strtok_r(testlist, ",", &saveptr); token != NULL; - token = strtok_r(NULL, ",", &saveptr)) { - - int found = 0; - for (j = 0; j < maxtok; j++) { - if (strcmp(token, test_list[j].name) == 0) { - test_list[j].run_test = 1; - found = 1; - } - } - if (!found) { - fprintf(stderr, "Test '%s' is not valid\n", token); - return PJ_ENOTFOUND; - } - } - - return PJ_SUCCESS; -} - -void list_tests(void) { - int maxtok = PJ_ARRAY_SIZE(test_list); - int j; - - fprintf(stderr, "Valid tests:\n"); - - for (j = 0; j < maxtok; j++) { - fprintf(stderr, " %s\n", test_list[j].name); - } -} - -#define SHOULD_RUN_TEST(ix) (run_all_tests || test_list[ix].run_test) +struct test_app_t test_app; +struct tsx_test_param tsx_test[MAX_TSX_TESTS]; void app_perror(const char *msg, pj_status_t rc) { @@ -168,6 +68,40 @@ void flush_events(unsigned duration) } } +/* Wait until there is no loop transport instance */ +pjsip_transport *wait_loop_transport_clear(int secs) +{ + pj_time_val timeout; + + pj_gettimeofday(&timeout); + timeout.sec += secs; + + for (;;) { + pj_sockaddr_in addr; + pjsip_transport *loop = NULL; + pj_time_val now; + pj_status_t status; + + loop = NULL; + pj_bzero(&addr, sizeof(addr)); + status = pjsip_endpt_acquire_transport(endpt, + PJSIP_TRANSPORT_LOOP_DGRAM, + &addr, sizeof(addr), NULL, + &loop); + if (status!=PJ_SUCCESS) + return NULL; + + pj_gettimeofday(&now); + if (PJ_TIME_VAL_GTE(now, timeout)) { + return loop; + } + + pjsip_transport_dec_ref(loop); + loop = NULL; + flush_events(500); + } +} + pj_status_t register_static_modules(pj_size_t *count, pjsip_module **modules) { PJ_UNUSED_ARG(modules); @@ -309,13 +243,11 @@ static void close_report(void) } -int test_main(char *testlist) +int test_main(int argc, char *argv[]) { pj_status_t rc; const char *filename; unsigned tsx_test_cnt=0; - struct tsx_test_param tsx_test[10]; - pj_status_t status; #if INCLUDE_TSX_TEST unsigned i; pjsip_transport *tp; @@ -325,187 +257,154 @@ int test_main(char *testlist) #endif /* INCLUDE_TSX_TEST */ int line; - rc = select_tests(testlist); - if (rc != PJ_SUCCESS) { - list_tests(); - return rc; - } - pj_log_set_level(log_level); pj_log_set_decor(param_log_decor); - if ((rc=pj_init()) != PJ_SUCCESS) { - app_perror("pj_init", rc); - return rc; - } + PJ_TEST_SUCCESS(pj_init(), NULL, return 10); + PJ_TEST_SUCCESS(pjlib_util_init(), NULL, return 20); + PJ_TEST_SUCCESS(init_report(), NULL, return 30); - if ((rc=pjlib_util_init()) != PJ_SUCCESS) { - app_perror("pj_init", rc); - return rc; + if (test_app.ut_app.prm_config) { + pj_dump_config(); } - status = init_report(); - if (status != PJ_SUCCESS) - return status; - - pj_dump_config(); - pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, PJSIP_TEST_MEM_SIZE ); - rc = pjsip_endpt_create(&caching_pool.factory, "endpt", &endpt); - if (rc != PJ_SUCCESS) { - app_perror("pjsip_endpt_create", rc); - pj_caching_pool_destroy(&caching_pool); - return rc; - } - - PJ_LOG(3,(THIS_FILE," ")); + PJ_TEST_SUCCESS(pjsip_endpt_create(&caching_pool.factory, "endpt", &endpt), + NULL, { + pj_caching_pool_destroy(&caching_pool); + return 40; + }); /* Init logger module. */ init_msg_logger(); msg_logger_set_enabled(1); /* Start transaction layer module. */ - rc = pjsip_tsx_layer_init_module(endpt); - if (rc != PJ_SUCCESS) { - app_perror(" Error initializing transaction module", rc); - goto on_return; - } + PJ_TEST_SUCCESS(rc = pjsip_tsx_layer_init_module(endpt), + NULL, goto on_return); - /* Create loop transport. */ - rc = pjsip_loop_start(endpt, NULL); - if (rc != PJ_SUCCESS) { - app_perror(" error: unable to create datagram loop transport", - rc); - goto on_return; - } tsx_test[tsx_test_cnt].port = 5060; tsx_test[tsx_test_cnt].tp_type = "loop-dgram"; tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_LOOP_DGRAM; ++tsx_test_cnt; + PJ_TEST_SUCCESS(rc=ut_app_init1(&test_app.ut_app, &caching_pool.factory), + NULL, goto on_return); #if INCLUDE_URI_TEST - if (SHOULD_RUN_TEST(include_uri_test)) { - DO_TEST(uri_test()); - } + UT_ADD_TEST(&test_app.ut_app, uri_test, 0); #endif #if INCLUDE_MSG_TEST - if (SHOULD_RUN_TEST(include_msg_test)) { - DO_TEST(msg_test()); - DO_TEST(msg_err_test()); - } + UT_ADD_TEST(&test_app.ut_app, msg_test, 0); + UT_ADD_TEST(&test_app.ut_app, msg_err_test, 0); #endif #if INCLUDE_MULTIPART_TEST - if (SHOULD_RUN_TEST(include_multipart_test)) { - DO_TEST(multipart_test()); - } + UT_ADD_TEST(&test_app.ut_app, multipart_test, 0); #endif #if INCLUDE_TXDATA_TEST - if (SHOULD_RUN_TEST(include_txdata_test)) { - DO_TEST(txdata_test()); - } + UT_ADD_TEST(&test_app.ut_app, txdata_test, 0); #endif #if INCLUDE_TSX_BENCH - if (SHOULD_RUN_TEST(include_tsx_bench)) { - DO_TEST(tsx_bench()); - } -#endif - -#if INCLUDE_UDP_TEST - if (SHOULD_RUN_TEST(include_udp_test)) { - DO_TEST(transport_udp_test()); - } + UT_ADD_TEST(&test_app.ut_app, tsx_bench, 0); #endif #if INCLUDE_LOOP_TEST - if (SHOULD_RUN_TEST(include_loop_test)) { - DO_TEST(transport_loop_test()); - } -#endif - -#if INCLUDE_TCP_TEST - if (SHOULD_RUN_TEST(include_tcp_test)) { - DO_TEST(transport_tcp_test()); - } + UT_ADD_TEST(&test_app.ut_app, transport_loop_multi_test, 0); #endif #if INCLUDE_RESOLVE_TEST - if (SHOULD_RUN_TEST(include_resolve_test)) { - DO_TEST(resolve_test()); - } + UT_ADD_TEST(&test_app.ut_app, resolve_test, 0); #endif -#if INCLUDE_LOOP_TEST - /* repeat again after resolver is configured in resolve_test() */ - if (SHOULD_RUN_TEST(include_loop_test)) { - DO_TEST(transport_loop_test()); - } +#if INCLUDE_INV_OA_TEST + UT_ADD_TEST(&test_app.ut_app, inv_offer_answer_test, 0); #endif #if INCLUDE_TSX_TEST - if (SHOULD_RUN_TEST(include_tsx_test)) { - status = pjsip_udp_transport_start(endpt, NULL, NULL, 1, &tp); - if (status == PJ_SUCCESS) { - tsx_test[tsx_test_cnt].port = tp->local_name.port; - tsx_test[tsx_test_cnt].tp_type = "udp"; - tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_UDP; - ++tsx_test_cnt; - } + PJ_TEST_SUCCESS(rc=pjsip_udp_transport_start(endpt, NULL, NULL, 1, &tp), + NULL, goto on_return); + tsx_test[tsx_test_cnt].port = tp->local_name.port; + tsx_test[tsx_test_cnt].tp_type = "udp"; + tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_UDP; + ++tsx_test_cnt; #if PJ_HAS_TCP - status = pjsip_tcp_transport_start(endpt, NULL, 1, &tpfactory); - if (status == PJ_SUCCESS) { - tsx_test[tsx_test_cnt].port = tpfactory->addr_name.port; - tsx_test[tsx_test_cnt].tp_type = "tcp"; - tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_TCP; - ++tsx_test_cnt; - } else { - app_perror("Unable to create TCP", status); - rc = -4; - goto on_return; - } + PJ_TEST_SUCCESS(rc=pjsip_tcp_transport_start(endpt, NULL, 1, &tpfactory), + NULL, goto on_return); + tsx_test[tsx_test_cnt].port = tpfactory->addr_name.port; + tsx_test[tsx_test_cnt].tp_type = "tcp"; + tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_TCP; + ++tsx_test_cnt; #endif - for (i = 0; i < tsx_test_cnt; ++i) { - DO_TSX_TEST(tsx_basic_test, &tsx_test[i]); - DO_TSX_TEST(tsx_uac_test, &tsx_test[i]); - DO_TSX_TEST(tsx_uas_test, &tsx_test[i]); - } + pj_assert(tsx_test[0].type == PJSIP_TRANSPORT_LOOP_DGRAM); + + for (i = 0; i < tsx_test_cnt; ++i) { + UT_ADD_TEST1(&test_app.ut_app, tsx_basic_test, (void*)(long)i, 0); + UT_ADD_TEST1(&test_app.ut_app, tsx_uac_test, (void*)(long)i, 0); + UT_ADD_TEST1(&test_app.ut_app, tsx_uas_test, (void*)(long)i, 0); } #endif -#if INCLUDE_INV_OA_TEST - if (SHOULD_RUN_TEST(include_inv_oa_test)) { - DO_TEST(inv_offer_answer_test()); - } +#if INCLUDE_LOOP_TEST + UT_ADD_TEST(&test_app.ut_app, transport_loop_test, 0); +#endif + +#if INCLUDE_UDP_TEST + /* Transport tests share same testing codes which are not reentrant */ + UT_ADD_TEST(&test_app.ut_app, transport_udp_test, 0); #endif +#if INCLUDE_TCP_TEST + UT_ADD_TEST(&test_app.ut_app, transport_tcp_test, 0); +#endif + + /* Note: put exclusive tests last */ + + /* + * regc_test() needs exclusive because it modifies pjsip_cfg() + */ #if INCLUDE_REGC_TEST - if (SHOULD_RUN_TEST(include_regc_test)) { - DO_TEST(regc_test()); - } + UT_ADD_TEST(&test_app.ut_app, regc_test, PJ_TEST_EXCLUSIVE | PJ_TEST_KEEP_LAST); +#endif + + /* This needs to be exclusive, because there must NOT be any other + * loop transport otherwise some test will fail (e.g. sending will + * fallback to that transport) + */ +#if INCLUDE_LOOP_TEST + UT_ADD_TEST(&test_app.ut_app, transport_loop_resolve_error_test, + PJ_TEST_EXCLUSIVE | PJ_TEST_KEEP_LAST); #endif /* * Better be last because it recreates the endpt */ #if INCLUDE_TSX_DESTROY_TEST - if (SHOULD_RUN_TEST(include_tsx_destroy_test)) { - DO_TEST(tsx_destroy_test()); - } + UT_ADD_TEST(&test_app.ut_app, tsx_destroy_test, + PJ_TEST_EXCLUSIVE | PJ_TEST_KEEP_LAST); #endif + if (ut_run_tests(&test_app.ut_app, "pjsip tests", argc, argv)) { + rc = 99; + } else { + rc = 0; + } + + ut_app_destroy(&test_app.ut_app); + on_return: flush_events(500); /* Show additional info on the log. e.g: not released memory pool. */ - pj_log_set_level(4); + // Commented out to make the output tidy :D (blp) + //pj_log_set_level(4); /* Dumping memory pool usage */ /* Field peak_used_size is deprecated by #3897 */ diff --git a/pjsip/src/test/test.h b/pjsip/src/test/test.h index 4fd8793c70..7fac3e3c61 100644 --- a/pjsip/src/test/test.h +++ b/pjsip/src/test/test.h @@ -20,6 +20,8 @@ #define __TEST_H__ #include +#include +#include extern pjsip_endpoint *endpt; extern pj_caching_pool caching_pool; @@ -83,9 +85,14 @@ int tsx_bench(void); int tsx_destroy_test(void); int transport_udp_test(void); int transport_loop_test(void); +int transport_loop_multi_test(void); +int transport_loop_resolve_error_test(void); int transport_tcp_test(void); int resolve_test(void); int regc_test(void); +int inv_offer_answer_test(void); + +#define MAX_TSX_TESTS 10 struct tsx_test_param { @@ -93,42 +100,60 @@ struct tsx_test_param int port; char *tp_type; }; +extern struct tsx_test_param tsx_test[MAX_TSX_TESTS]; -int tsx_basic_test(struct tsx_test_param *param); -int tsx_uac_test(struct tsx_test_param *param); -int tsx_uas_test(struct tsx_test_param *param); +int tsx_basic_test(unsigned tid); +int tsx_uac_test(unsigned tid); +int tsx_uas_test(unsigned tid); /* Transport test helpers (transport_test.c). */ int generic_transport_test(pjsip_transport *tp); int transport_send_recv_test( pjsip_transport_type_e tp_type, pjsip_transport *ref_tp, - char *target_url, + const char *host_port_transport, int *p_usec_rtt); int transport_rt_test( pjsip_transport_type_e tp_type, pjsip_transport *ref_tp, - char *target_url, + const char *host_port_transport, int *pkt_lost); -int transport_load_test(char *target_url); - -/* Invite session */ -int inv_offer_answer_test(void); +int transport_load_test(pjsip_transport_type_e tp_type, + const char *host_port_transport); /* Test main entry */ -int test_main(char *testlist); +int test_main(int argc, char *argv[]); /* Test utilities. */ -void list_tests(void); void app_perror(const char *msg, pj_status_t status); int init_msg_logger(void); int msg_logger_set_enabled(pj_bool_t enabled); void flush_events(unsigned duration); - +pjsip_transport *wait_loop_transport_clear(int secs); void report_ival(const char *name, int value, const char *valname, const char *desc); void report_sval(const char *name, const char* value, const char *valname, const char *desc); +/* Utility to check if the user part of From/To is equal to the string */ +PJ_INLINE(pj_bool_t) is_user_equal(const pjsip_fromto_hdr *hdr, const char *user) +{ + const pjsip_sip_uri *sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(hdr->uri); + const pj_str_t *scheme = pjsip_uri_get_scheme(sip_uri); + + if (pj_stricmp2(scheme, "sip") && pj_stricmp2(scheme, "sips")) + return PJ_FALSE; + + return pj_strcmp2(&sip_uri->user, user)==0; +} /* Settings. */ extern int log_level; +#define UT_MAX_TESTS 32 +#include "../../../pjlib/src/pjlib-test/test_util.h" + +struct test_app_t +{ + ut_app_t ut_app; +}; +extern struct test_app_t test_app; + #endif /* __TEST_H__ */ diff --git a/pjsip/src/test/transport_loop_test.c b/pjsip/src/test/transport_loop_test.c index 40175160c5..dbd6f79b28 100644 --- a/pjsip/src/test/transport_loop_test.c +++ b/pjsip/src/test/transport_loop_test.c @@ -23,6 +23,157 @@ #define THIS_FILE "transport_loop_test.c" +static pjsip_transport *cur_loop; +static int loop_test_status; + +static pj_bool_t on_rx_request(pjsip_rx_data *rdata); +static pj_bool_t on_rx_response(pjsip_rx_data *rdata); +static pj_status_t on_tx_message(pjsip_tx_data *tdata); + +static pjsip_module loop_tester_mod = +{ + NULL, NULL, /* prev and next */ + { "transport_loop_test", 19}, /* Name. */ + -1, /* Id */ + PJSIP_MOD_PRIORITY_UA_PROXY_LAYER-1,/* Priority */ + NULL, /* load() */ + NULL, /* start() */ + NULL, /* stop() */ + NULL, /* unload() */ + &on_rx_request, /* on_rx_request() */ + &on_rx_response, /* on_rx_response() */ + &on_tx_message, /* on_tx_request() */ + &on_tx_message, /* on_tx_response() */ + NULL, /* on_tsx_state() */ +}; + + +static pj_bool_t on_rx_request(pjsip_rx_data *rdata) +{ +#define ERR(rc__) {loop_test_status=rc__; return PJ_TRUE; } + if (!is_user_equal(rdata->msg_info.from, "transport_loop_multi_test")) + return PJ_FALSE; + + PJ_TEST_EQ(rdata->tp_info.transport, cur_loop, NULL, ERR(-100)); + PJ_TEST_SUCCESS(pjsip_endpt_respond_stateless(endpt, rdata, PJSIP_SC_ACCEPTED, + NULL, NULL, NULL), + NULL, ERR(-100)); + return PJ_TRUE; +#undef ERR +} + +static pj_bool_t on_rx_response(pjsip_rx_data *rdata) +{ +#define ERR(rc__) {loop_test_status=rc__; return PJ_TRUE; } + if (!is_user_equal(rdata->msg_info.from, "transport_loop_multi_test")) + return PJ_FALSE; + + PJ_TEST_EQ(rdata->tp_info.transport, cur_loop, NULL, ERR(-150)); + PJ_TEST_EQ(rdata->msg_info.msg->line.status.code, PJSIP_SC_ACCEPTED, NULL, + ERR(-160)); + + loop_test_status = 0; + return PJ_TRUE; +#undef ERR +} + +static pj_status_t on_tx_message(pjsip_tx_data *tdata) +{ + pjsip_from_hdr *from_hdr = (pjsip_from_hdr*) + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL); + + if (!is_user_equal(from_hdr, "transport_loop_multi_test")) + return PJ_SUCCESS; + + PJ_TEST_EQ(tdata->tp_info.transport, cur_loop, + tdata->msg->type==PJSIP_REQUEST_MSG? + "request transport mismatch" : "response transport mismatch", + loop_test_status=-200); + + return PJ_SUCCESS; +} + +/* Test that request and responses are sent/received on the correct loop + * instance when multiple instances of transport loop are created + */ +int transport_loop_multi_test(void) +{ +#define ERR(rc__) { rc=rc__; goto on_return; } + enum { N = 4, START_PORT=5000, TIMEOUT=1000 }; + pjsip_transport *loops[N]; + int i, rc; + + PJ_TEST_SUCCESS(pjsip_endpt_register_module(endpt, &loop_tester_mod), + NULL, ERR(-5)); + + pj_bzero(loops, sizeof(loops)); + for (i=0; itoken; @@ -40,7 +191,7 @@ static void send_cb(pjsip_send_state *st, pj_ssize_t sent, pj_bool_t *cont) } } -static int loop_resolve_error() +static int loop_resolve_error(pj_bool_t disable_no_selector) { pjsip_transport *loop = NULL; pj_sockaddr_in addr; @@ -50,6 +201,8 @@ static int loop_resolve_error() int i, loop_resolve_status; pj_status_t status; + PJ_LOG(3,(THIS_FILE, "testing loop resolve error")); + loop_resolve_status = 0; pj_bzero(&addr, sizeof(addr)); @@ -60,9 +213,12 @@ static int loop_resolve_error() return -210; } - url = pj_str("sip:bob@unresolved-host"); + url = pj_str("sip:bob@unresolved-host;transport=loop-dgram"); for (i=0; i<2; ++i) { + if (i==0 && disable_no_selector) + continue; + /* variant a: without tp_selector */ status = pjsip_endpt_create_request( endpt, &pjsip_options_method, &url, &url, &url, NULL, NULL, -1, @@ -89,14 +245,20 @@ static int loop_resolve_error() /* Success! (we're expecting error and got immediate error)*/ loop_resolve_status = 0; } else { - flush_events(500); + unsigned j; + + for (j=0; j<40 && loop_resolve_status==PJ_EPENDING; ++j) + flush_events(500); + if (loop_resolve_status!=PJ_EPENDING && loop_resolve_status!=PJ_SUCCESS) { /* Success! (we're expecting error in callback)*/ //PJ_PERROR(3, (THIS_FILE, loop_resolve_status, // " correctly got error")); loop_resolve_status = 0; } else { - PJ_LOG(3,(THIS_FILE, " error: expecting error but status=%d", status)); + PJ_LOG(3,(THIS_FILE, + " error (variant %d): expecting error but loop_resolve_status=%d", + i, loop_resolve_status)); loop_resolve_status = -240; goto on_return; } @@ -107,29 +269,27 @@ static int loop_resolve_error() if (loop) pjsip_transport_dec_ref(loop); return loop_resolve_status; - } -static int datagram_loop_test() +int transport_loop_test() { enum { LOOP = 8 }; - pjsip_transport *loop; + pjsip_transport *loop = NULL; + char host_port_transport[64]; int i, pkt_lost; - pj_sockaddr_in addr; - pj_status_t status; + int status; long ref_cnt; int rtt[LOOP], min_rtt; - PJ_LOG(3,(THIS_FILE, "testing datagram loop transport")); - pj_sockaddr_in_init(&addr, NULL, 0); - /* Test acquire transport. */ - status = pjsip_endpt_acquire_transport( endpt, PJSIP_TRANSPORT_LOOP_DGRAM, - &addr, sizeof(addr), NULL, &loop); - if (status != PJ_SUCCESS) { - app_perror(" error: loop transport is not configured", status); - return -20; - } + PJ_TEST_SUCCESS(pjsip_loop_start(endpt, &loop), NULL, return -3); + pjsip_transport_add_ref(loop); + + pj_ansi_snprintf(host_port_transport, sizeof(host_port_transport), + "%.*s:%d;transport=loop-dgram", + (int)loop->local_name.host.slen, + loop->local_name.host.ptr, + loop->local_name.port); /* Get initial reference counter */ ref_cnt = pj_atomic_get(loop->ref_cnt); @@ -137,22 +297,16 @@ static int datagram_loop_test() /* Test basic transport attributes */ status = generic_transport_test(loop); if (status != PJ_SUCCESS) - return status; + goto on_return; /* Basic transport's send/receive loopback test. */ for (i=0; iref_cnt) != ref_cnt) { PJ_LOG(3,(THIS_FILE, " error: ref counter is not %ld (%ld)", ref_cnt, pj_atomic_get(loop->ref_cnt))); - return -51; + status = -51; + goto on_return; } + status = 0; + +on_return: /* Decrement reference. */ pjsip_transport_dec_ref(loop); - - return 0; + return status; } -int transport_loop_test(void) + +/* tgus needs to be exclusive, because there must NOT be any other + * loop transport otherwise some test will fail (e.g. sending will + * fallback to that transport) + */ +int transport_loop_resolve_error_test() { + pjsip_transport *loop; + pj_bool_t disable_no_selector = PJ_FALSE; int status; - status = datagram_loop_test(); - if (status != 0) - return status; + PJ_LOG(3,(THIS_FILE, " Loop transport count: %d", + pjsip_tpmgr_get_transport_count_by_type( + pjsip_endpt_get_tpmgr(endpt), + PJSIP_TRANSPORT_LOOP_DGRAM))); + + /* if there is another transport, wait sometime until it's gone */ + loop = wait_loop_transport_clear(10); + + if (loop) { + /* We are not supposed to have another instance of loop transport, but + * we found one. If there are more than one, test will fail. Otherwise + * test should succeed + */ + PJ_LOG(2,(THIS_FILE, + "Warning: found %d loop transport instances", + pjsip_tpmgr_get_transport_count_by_type( + pjsip_endpt_get_tpmgr(endpt), + PJSIP_TRANSPORT_LOOP_DGRAM))); + disable_no_selector = PJ_TRUE; + pjsip_loop_set_discard(loop, 0, NULL); + pjsip_loop_set_failure(loop, 0, NULL); + pjsip_loop_set_delay(loop, 0); + } else { + PJ_TEST_EQ(loop, NULL, NULL, return -2); + PJ_TEST_SUCCESS(pjsip_loop_start(endpt, &loop), NULL, return -3); + pjsip_transport_add_ref(loop); + } + + /* Resolve error */ + status = loop_resolve_error(disable_no_selector); - return 0; +on_return: + /* Decrement reference. */ + pjsip_transport_dec_ref(loop); + return status; } diff --git a/pjsip/src/test/transport_tcp_test.c b/pjsip/src/test/transport_tcp_test.c index c3da6bd319..4fdc98906f 100644 --- a/pjsip/src/test/transport_tcp_test.c +++ b/pjsip/src/test/transport_tcp_test.c @@ -156,7 +156,7 @@ int transport_tcp_test(void) pjsip_transport *tcp[NUM_TP]; pj_sockaddr_in rem_addr; pj_status_t status; - char url[PJSIP_MAX_URL_SIZE]; + char host_port_param[PJSIP_MAX_URL_SIZE]; char addr[PJ_INET_ADDRSTRLEN]; int rtt[SEND_RECV_LOOP], min_rtt; int pkt_lost; @@ -172,19 +172,16 @@ int transport_tcp_test(void) status = pj_sockaddr_in_init(&rem_addr, &tpfactory[0]->addr_name.host, (pj_uint16_t)tpfactory[0]->addr_name.port); - pj_ansi_snprintf(url, sizeof(url), "sip:alice@%s:%d;transport=tcp", + pj_ansi_snprintf(host_port_param, sizeof(host_port_param), + "%s:%d;transport=tcp", pj_inet_ntop2(pj_AF_INET(), &rem_addr.sin_addr, addr, sizeof(addr)), pj_ntohs(rem_addr.sin_port)); - /* Load test */ - if (transport_load_test(url) != 0) - return -60; - /* Basic transport's send/receive loopback test. */ for (i=0; iref_cnt) != 1) diff --git a/pjsip/src/test/transport_test.c b/pjsip/src/test/transport_test.c index f834d1dec9..6f4fda98aa 100644 --- a/pjsip/src/test/transport_test.c +++ b/pjsip/src/test/transport_test.c @@ -39,10 +39,8 @@ int generic_transport_test(pjsip_transport *tp) if (pj_inet_pton(pj_AF_INET(), &tp->local_name.host, &addr) == PJ_SUCCESS) { - if (addr.s_addr==PJ_INADDR_ANY || addr.s_addr==PJ_INADDR_NONE) { - PJ_LOG(3,(THIS_FILE, " Error: invalid address name")); - return -420; - } + PJ_TEST_TRUE(addr.s_addr!=PJ_INADDR_ANY && addr.s_addr!=PJ_INADDR_NONE, + "invalid address name", return -420); } else { /* It's okay. local_name.host may be a hostname instead of * IP address. @@ -51,17 +49,13 @@ int generic_transport_test(pjsip_transport *tp) } /* Check that port is valid. */ - if (tp->local_name.port <= 0) { - return -430; - } + PJ_TEST_GT(tp->local_name.port, 0, NULL, return -430); /* Check length of address (for now we only check against sockaddr_in). */ - if (tp->addr_len != sizeof(pj_sockaddr_in)) - return -440; + PJ_TEST_EQ(tp->addr_len, sizeof(pj_sockaddr_in), NULL, return -440); /* Check type. */ - if (tp->key.type == PJSIP_TRANSPORT_UNSPECIFIED) - return -450; + PJ_TEST_NEQ(tp->key.type, PJSIP_TRANSPORT_UNSPECIFIED, NULL, return -450); /* That's it. */ return PJ_SUCCESS; @@ -77,11 +71,12 @@ int generic_transport_test(pjsip_transport *tp) * The main purpose is to test that the basic transport functionalities works, * before we continue with more complicated tests. */ -#define FROM_HDR "Bob " -#define CONTACT_HDR "Bob " -#define CALL_ID_HDR "SendRecv-Test" -#define CSEQ_VALUE 100 -#define BODY "Hello World!" +#define SEND_RECV_FROM_HDR "Bob " +#define SEND_RECV_CALL_ID_HDR "Transport-SendRecv-Test" +#define RT_FROM_HDR "Bob " +#define CONTACT_HDR "Bob " +#define CSEQ_VALUE 100 +#define BODY "Hello World!" static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata); static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata); @@ -90,12 +85,18 @@ static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata); * (or failed to send) */ #define NO_STATUS -2 -static int send_status = NO_STATUS; -static int recv_status = NO_STATUS; -static pj_timestamp my_send_time, my_recv_time; + +struct send_recv_test_global_t +{ + int send_status; + int recv_status; + pj_timestamp my_send_time; + pj_timestamp my_recv_time; +}; +static struct send_recv_test_global_t sr_g[PJSIP_TRANSPORT_START_OTHER]; /* Module to receive messages for this test. */ -static pjsip_module my_module = +static pjsip_module send_recv_module = { NULL, NULL, /* prev and next */ { "Transport-Test", 14}, /* Name. */ @@ -113,8 +114,18 @@ static pjsip_module my_module = static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata) { + pjsip_to_hdr *to_hdr = rdata->msg_info.to; + pjsip_sip_uri *target = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri); + unsigned tid; + + if (!is_user_equal(rdata->msg_info.from, "transport_send_recv_test")) + return PJ_FALSE; + + tid = (unsigned)pj_strtoul(&target->user); + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); + /* Check that this is our request. */ - if (pj_strcmp2(&rdata->msg_info.cid->id, CALL_ID_HDR) == 0) { + if (pj_strcmp2(&rdata->msg_info.cid->id, SEND_RECV_CALL_ID_HDR) == 0) { /* It is! */ /* Send response. */ pjsip_tx_data *tdata; @@ -123,18 +134,18 @@ static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata) status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata); if (status != PJ_SUCCESS) { - recv_status = status; + sr_g[tid].recv_status = status; return PJ_TRUE; } status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr); if (status != PJ_SUCCESS) { - recv_status = status; + sr_g[tid].recv_status = status; pjsip_tx_data_dec_ref(tdata); return PJ_TRUE; } status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL); if (status != PJ_SUCCESS) { - recv_status = status; + sr_g[tid].recv_status = status; pjsip_tx_data_dec_ref(tdata); return PJ_TRUE; } @@ -147,9 +158,19 @@ static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata) static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata) { - if (pj_strcmp2(&rdata->msg_info.cid->id, CALL_ID_HDR) == 0) { - pj_get_timestamp(&my_recv_time); - recv_status = PJ_SUCCESS; + pjsip_to_hdr *to_hdr = rdata->msg_info.to; + pjsip_sip_uri *target = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri); + unsigned tid; + + if (!is_user_equal(rdata->msg_info.from, "transport_send_recv_test")) + return PJ_FALSE; + + tid = (unsigned)pj_strtoul(&target->user); + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); + + if (pj_strcmp2(&rdata->msg_info.cid->id, SEND_RECV_CALL_ID_HDR) == 0) { + pj_get_timestamp(&sr_g[tid].my_recv_time); + sr_g[tid].recv_status = PJ_SUCCESS; return PJ_TRUE; } return PJ_FALSE; @@ -159,13 +180,16 @@ static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata) static void send_msg_callback(pjsip_send_state *stateless_data, pj_ssize_t sent, pj_bool_t *cont) { - PJ_UNUSED_ARG(stateless_data); + unsigned tid = (unsigned)(long)stateless_data->token; + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); if (sent < 1) { /* Obtain the error code. */ - send_status = (int)-sent; + PJ_LOG(3,(THIS_FILE, " Sending %s got callback error %ld", + stateless_data->tdata->info, -sent)); + sr_g[tid].send_status = (int)-sent; } else { - send_status = PJ_SUCCESS; + sr_g[tid].send_status = PJ_SUCCESS; } /* Don't want to continue. */ @@ -176,10 +200,12 @@ static void send_msg_callback(pjsip_send_state *stateless_data, /* Test that we receive loopback message. */ int transport_send_recv_test( pjsip_transport_type_e tp_type, pjsip_transport *ref_tp, - char *target_url, + const char *host_port_transport, int *p_usec_rtt) { + unsigned tid = tp_type; pj_bool_t msg_log_enabled; + char target_url[64]; pj_status_t status; pj_str_t target, from, to, contact, call_id, body; pjsip_method method; @@ -192,23 +218,29 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type, PJ_LOG(3,(THIS_FILE, " single message round-trip test...")); /* Register out test module to receive the message (if necessary). */ - if (my_module.id == -1) { - status = pjsip_endpt_register_module( endpt, &my_module ); + pj_enter_critical_section(); + if (send_recv_module.id == -1) { + status = pjsip_endpt_register_module( endpt, &send_recv_module ); if (status != PJ_SUCCESS) { + pj_leave_critical_section(); app_perror(" error: unable to register module", status); return -500; } } + pj_leave_critical_section(); /* Disable message logging. */ msg_log_enabled = msg_logger_set_enabled(0); + pj_ansi_snprintf(target_url, sizeof(target_url), "sip:%d@%s", + tp_type, host_port_transport); + /* Create a request message. */ target = pj_str(target_url); - from = pj_str(FROM_HDR); + from = pj_str(SEND_RECV_FROM_HDR); to = pj_str(target_url); contact = pj_str(CONTACT_HDR); - call_id = pj_str(CALL_ID_HDR); + call_id = pj_str(SEND_RECV_CALL_ID_HDR); body = pj_str(BODY); pjsip_method_set(&method, PJSIP_OPTIONS_METHOD); @@ -221,20 +253,35 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type, } /* Reset statuses */ - send_status = recv_status = NO_STATUS; + sr_g[tid].send_status = sr_g[tid].recv_status = NO_STATUS; + + /* Need this to make sure we use our loop transport rather than other + * loop transport instances created by other test thread (which they + * can disappear at any time) + */ + if (tp_type==PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_tpselector tp_sel; + pj_bzero(&tp_sel, sizeof(tp_sel)); + tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; + tp_sel.u.transport = ref_tp; + + pjsip_tx_data_set_transport(tdata, &tp_sel); + } /* Start time. */ - pj_get_timestamp(&my_send_time); + pj_get_timestamp(&sr_g[tid].my_send_time); /* Send the message (statelessly). */ - PJ_LOG(5,(THIS_FILE, "Sending request to %.*s", + PJ_LOG(3,(THIS_FILE, "Sending request to %.*s", (int)target.slen, target.ptr)); - status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL, + status = pjsip_endpt_send_request_stateless( endpt, tdata, (void*)(long)tid, &send_msg_callback); if (status != PJ_SUCCESS) { /* Immediate error! */ + PJ_LOG(3,(THIS_FILE, " Sending %s to %.*s got immediate error %d", + tdata->info, (int)target.slen, target.ptr, status)); pjsip_tx_data_dec_ref(tdata); - send_status = status; + sr_g[tid].send_status = status; } /* Set the timeout (2 seconds from now) */ @@ -253,19 +300,19 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type, goto on_return; } - if (send_status!=NO_STATUS && send_status!=PJ_SUCCESS) { - app_perror(" error sending message", send_status); + if (sr_g[tid].send_status!=NO_STATUS && sr_g[tid].send_status!=PJ_SUCCESS) { + app_perror(" error sending message", sr_g[tid].send_status); status = -550; goto on_return; } - if (recv_status!=NO_STATUS && recv_status!=PJ_SUCCESS) { - app_perror(" error receiving message", recv_status); + if (sr_g[tid].recv_status!=NO_STATUS && sr_g[tid].recv_status!=PJ_SUCCESS) { + app_perror(" error receiving message", sr_g[tid].recv_status); status = -560; goto on_return; } - if (send_status!=NO_STATUS && recv_status!=NO_STATUS) { + if (sr_g[tid].send_status!=NO_STATUS && sr_g[tid].recv_status!=NO_STATUS) { /* Success! */ break; } @@ -276,7 +323,7 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type, if (status == PJ_SUCCESS) { unsigned usec_rt; - usec_rt = pj_elapsed_usec(&my_send_time, &my_recv_time); + usec_rt = pj_elapsed_usec(&sr_g[tid].my_send_time, &sr_g[tid].my_recv_time); PJ_LOG(3,(THIS_FILE, " round-trip = %d usec", usec_rt)); @@ -322,25 +369,46 @@ static pjsip_module rt_module = NULL, /* tsx_handler() */ }; -static struct +enum { - pj_thread_t *thread; - pj_timestamp send_time; - pj_timestamp total_rt_time; - int sent_request_count, recv_response_count; - pj_str_t call_id; - pj_timer_entry timeout_timer; - pj_timer_entry tx_timer; - pj_mutex_t *mutex; -} rt_test_data[16]; - -static char rt_target_uri[64]; -static pj_bool_t rt_stop; -static pj_str_t rt_call_id; + STOP_SEND = 1, + STOP_RECV = 2, +}; + +static struct rt_global_t { + struct { + pj_thread_t *thread; + pj_timestamp send_time; + pj_timestamp total_rt_time; + int sent_request_count, recv_response_count; + pj_str_t call_id; + pj_timer_entry timeout_timer; + pj_timer_entry tx_timer; + pj_mutex_t *mutex; + } rt_test_data[16]; + + char rt_target_uri[64]; + int rt_stop; + pj_str_t rt_call_id; + pjsip_transport *rt_transport; + +} g_rt[PJSIP_TRANSPORT_START_OTHER]; static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata) { - if (!pj_strncmp(&rdata->msg_info.cid->id, &rt_call_id, rt_call_id.slen)) { + pjsip_to_hdr *to_hdr = rdata->msg_info.to; + pjsip_sip_uri *target = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri); + unsigned tid; + + if (!is_user_equal(rdata->msg_info.from, "transport_rt_test")) + return PJ_FALSE; + + tid = (unsigned)pj_strtoul(&target->user); + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); + + if (!pj_strncmp(&rdata->msg_info.cid->id, &g_rt[tid].rt_call_id, + g_rt[tid].rt_call_id.slen)) + { pjsip_tx_data *tdata; pjsip_response_addr res_addr; pj_status_t status; @@ -368,21 +436,22 @@ static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata) return PJ_FALSE; } -static pj_status_t rt_send_request(int thread_id) +static pj_status_t rt_send_request(unsigned tid, int thread_id) { pj_status_t status; pj_str_t target, from, to, contact, call_id; pjsip_tx_data *tdata; pj_time_val timeout_delay; + pjsip_tpselector tp_sel; - pj_mutex_lock(rt_test_data[thread_id].mutex); + pj_mutex_lock(g_rt[tid].rt_test_data[thread_id].mutex); /* Create a request message. */ - target = pj_str(rt_target_uri); - from = pj_str(FROM_HDR); - to = pj_str(rt_target_uri); + target = pj_str(g_rt[tid].rt_target_uri); + from = pj_str(RT_FROM_HDR); + to = pj_str(g_rt[tid].rt_target_uri); contact = pj_str(CONTACT_HDR); - call_id = rt_test_data[thread_id].call_id; + call_id = g_rt[tid].rt_test_data[thread_id].call_id; status = pjsip_endpt_create_request( endpt, &pjsip_options_method, &target, &from, &to, @@ -390,12 +459,19 @@ static pj_status_t rt_send_request(int thread_id) NULL, &tdata ); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); - pj_mutex_unlock(rt_test_data[thread_id].mutex); + pj_mutex_unlock(g_rt[tid].rt_test_data[thread_id].mutex); return -610; } + if (g_rt[tid].rt_transport) { + pj_bzero(&tp_sel, sizeof(tp_sel)); + tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; + tp_sel.u.transport = g_rt[tid].rt_transport; + pjsip_tx_data_set_transport(tdata, &tp_sel); + + } /* Start time. */ - pj_get_timestamp(&rt_test_data[thread_id].send_time); + pj_get_timestamp(&g_rt[tid].rt_test_data[thread_id].send_time); /* Send the message (statelessly). */ status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL, NULL); @@ -403,54 +479,66 @@ static pj_status_t rt_send_request(int thread_id) /* Immediate error! */ app_perror(" error: send request", status); pjsip_tx_data_dec_ref(tdata); - pj_mutex_unlock(rt_test_data[thread_id].mutex); + pj_mutex_unlock(g_rt[tid].rt_test_data[thread_id].mutex); return -620; } /* Update counter. */ - rt_test_data[thread_id].sent_request_count++; + g_rt[tid].rt_test_data[thread_id].sent_request_count++; /* Set timeout timer. */ - if (rt_test_data[thread_id].timeout_timer.user_data != NULL) { - pjsip_endpt_cancel_timer(endpt, &rt_test_data[thread_id].timeout_timer); + if (g_rt[tid].rt_test_data[thread_id].timeout_timer.user_data != NULL) { + pjsip_endpt_cancel_timer(endpt, &g_rt[tid].rt_test_data[thread_id].timeout_timer); } timeout_delay.sec = 100; timeout_delay.msec = 0; - rt_test_data[thread_id].timeout_timer.user_data = (void*)(pj_ssize_t)1; - pjsip_endpt_schedule_timer(endpt, &rt_test_data[thread_id].timeout_timer, + g_rt[tid].rt_test_data[thread_id].timeout_timer.user_data = (void*)(pj_ssize_t)1; + pjsip_endpt_schedule_timer(endpt, &g_rt[tid].rt_test_data[thread_id].timeout_timer, &timeout_delay); - pj_mutex_unlock(rt_test_data[thread_id].mutex); + pj_mutex_unlock(g_rt[tid].rt_test_data[thread_id].mutex); return PJ_SUCCESS; } static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata) { - if (!pj_strncmp(&rdata->msg_info.cid->id, &rt_call_id, rt_call_id.slen)) { + pjsip_to_hdr *to_hdr = rdata->msg_info.to; + pjsip_sip_uri *target = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri); + unsigned tid; + + if (!is_user_equal(rdata->msg_info.from, "transport_rt_test")) + return PJ_FALSE; + + tid = (unsigned)pj_strtoul(&target->user); + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); + + if (!pj_strncmp(&rdata->msg_info.cid->id, &g_rt[tid].rt_call_id, + g_rt[tid].rt_call_id.slen)) + { char *pos = pj_strchr(&rdata->msg_info.cid->id, '/')+1; int thread_id = (*pos - '0'); pj_timestamp recv_time; - pj_mutex_lock(rt_test_data[thread_id].mutex); + pj_mutex_lock(g_rt[tid].rt_test_data[thread_id].mutex); /* Stop timer. */ - pjsip_endpt_cancel_timer(endpt, &rt_test_data[thread_id].timeout_timer); + pjsip_endpt_cancel_timer(endpt, &g_rt[tid].rt_test_data[thread_id].timeout_timer); /* Update counter and end-time. */ - rt_test_data[thread_id].recv_response_count++; + g_rt[tid].rt_test_data[thread_id].recv_response_count++; pj_get_timestamp(&recv_time); - pj_sub_timestamp(&recv_time, &rt_test_data[thread_id].send_time); - pj_add_timestamp(&rt_test_data[thread_id].total_rt_time, &recv_time); + pj_sub_timestamp(&recv_time, &g_rt[tid].rt_test_data[thread_id].send_time); + pj_add_timestamp(&g_rt[tid].rt_test_data[thread_id].total_rt_time, &recv_time); - if (!rt_stop) { + if ((g_rt[tid].rt_stop & STOP_SEND)==0) { pj_time_val tx_delay = { 0, 0 }; - pj_assert(rt_test_data[thread_id].tx_timer.user_data == NULL); - rt_test_data[thread_id].tx_timer.user_data = (void*)(pj_ssize_t)1; - pjsip_endpt_schedule_timer(endpt, &rt_test_data[thread_id].tx_timer, + pj_assert(g_rt[tid].rt_test_data[thread_id].tx_timer.user_data == NULL); + g_rt[tid].rt_test_data[thread_id].tx_timer.user_data = (void*)(pj_ssize_t)1; + pjsip_endpt_schedule_timer(endpt, &g_rt[tid].rt_test_data[thread_id].tx_timer, &tx_delay); } - pj_mutex_unlock(rt_test_data[thread_id].mutex); + pj_mutex_unlock(g_rt[tid].rt_test_data[thread_id].mutex); return PJ_TRUE; } @@ -460,64 +548,76 @@ static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata) static void rt_timeout_timer( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry ) { - pj_mutex_lock(rt_test_data[entry->id].mutex); + unsigned tid = entry->id >> 16; + unsigned thread_id = entry->id & 0xFFFF; + + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); + pj_mutex_lock(g_rt[tid].rt_test_data[thread_id].mutex); PJ_UNUSED_ARG(timer_heap); PJ_LOG(3,(THIS_FILE, " timeout waiting for response")); - rt_test_data[entry->id].timeout_timer.user_data = NULL; + g_rt[tid].rt_test_data[thread_id].timeout_timer.user_data = NULL; - if (rt_test_data[entry->id].tx_timer.user_data == NULL) { + if (g_rt[tid].rt_test_data[thread_id].tx_timer.user_data == NULL) { pj_time_val delay = { 0, 0 }; - rt_test_data[entry->id].tx_timer.user_data = (void*)(pj_ssize_t)1; - pjsip_endpt_schedule_timer(endpt, &rt_test_data[entry->id].tx_timer, + g_rt[tid].rt_test_data[thread_id].tx_timer.user_data = (void*)(pj_ssize_t)1; + pjsip_endpt_schedule_timer(endpt, &g_rt[tid].rt_test_data[thread_id].tx_timer, &delay); } - pj_mutex_unlock(rt_test_data[entry->id].mutex); + pj_mutex_unlock(g_rt[tid].rt_test_data[thread_id].mutex); } static void rt_tx_timer( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry ) { - pj_mutex_lock(rt_test_data[entry->id].mutex); + unsigned tid = entry->id >> 16; + unsigned thread_id = entry->id & 0xFFFF; + + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); + pj_mutex_lock(g_rt[tid].rt_test_data[thread_id].mutex); PJ_UNUSED_ARG(timer_heap); - pj_assert(rt_test_data[entry->id].tx_timer.user_data != NULL); - rt_test_data[entry->id].tx_timer.user_data = NULL; - rt_send_request(entry->id); + pj_assert(g_rt[tid].rt_test_data[thread_id].tx_timer.user_data != NULL); + g_rt[tid].rt_test_data[thread_id].tx_timer.user_data = NULL; + rt_send_request(tid, thread_id); - pj_mutex_unlock(rt_test_data[entry->id].mutex); + pj_mutex_unlock(g_rt[tid].rt_test_data[thread_id].mutex); } static int rt_worker_thread(void *arg) { + unsigned tid; int i; pj_time_val poll_delay = { 0, 10 }; - PJ_UNUSED_ARG(arg); + tid = (unsigned)(long)arg; + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); /* Sleep to allow main threads to run. */ pj_thread_sleep(10); - while (!rt_stop) { + while ((g_rt[tid].rt_stop & STOP_RECV)==0) { pjsip_endpt_handle_events(endpt, &poll_delay); } /* Exhaust responses. */ - for (i=0; i<100; ++i) + for (i=0; i<100; ++i) { pjsip_endpt_handle_events(endpt, &poll_delay); + } return 0; } int transport_rt_test( pjsip_transport_type_e tp_type, pjsip_transport *ref_tp, - char *target_url, + const char *host_port_transport, int *lost) { - enum { THREADS = 4, INTERVAL = 10 }; - int i; + enum { THREADS = 1, INTERVAL = 120 }; + int i, tid=tp_type; + char target_url[80]; pj_status_t status; pj_pool_t *pool; pj_bool_t logger_enabled; @@ -539,13 +639,16 @@ int transport_rt_test( pjsip_transport_type_e tp_type, logger_enabled = msg_logger_set_enabled(0); /* Register module (if not yet registered) */ + pj_enter_critical_section(); if (rt_module.id == -1) { status = pjsip_endpt_register_module( endpt, &rt_module ); if (status != PJ_SUCCESS) { + pj_leave_critical_section(); app_perror(" error: unable to register module", status); return -600; } } + pj_leave_critical_section(); /* Create pool for this test. */ pool = pjsip_endpt_create_pool(endpt, NULL, 4000, 4000); @@ -553,9 +656,18 @@ int transport_rt_test( pjsip_transport_type_e tp_type, return -610; /* Initialize static test data. */ - pj_ansi_strxcpy(rt_target_uri, target_url, sizeof(rt_target_uri)); - rt_call_id = pj_str("RT-Call-Id/"); - rt_stop = PJ_FALSE; + pj_ansi_snprintf(target_url, sizeof(target_url), "sip:%d@%s", + tp_type, host_port_transport); + pj_ansi_strxcpy(g_rt[tid].rt_target_uri, target_url, + sizeof(g_rt[tid].rt_target_uri)); + g_rt[tid].rt_call_id = pj_str("RT-Call-Id/"); + g_rt[tid].rt_stop = 0; + + pj_bzero(g_rt[tid].rt_test_data, sizeof(g_rt[tid].rt_test_data)); + + /* Need to use specific loop transport (otherwise we may get transport + * that is being shutdown) */ + g_rt[tid].rt_transport = tp_type==PJSIP_TRANSPORT_LOOP_DGRAM? ref_tp : NULL; /* Initialize thread data. */ for (i=0; imsg_info.cseq->cseq != mod_load.next_seq) { - PJ_LOG(1,("THIS_FILE", " err: expecting cseq %u, got %u", - mod_load.next_seq, rdata->msg_info.cseq->cseq)); - mod_load.err = PJ_TRUE; - mod_load.next_seq = rdata->msg_info.cseq->cseq + 1; + pjsip_to_hdr *to_hdr = rdata->msg_info.to; + pjsip_sip_uri *target = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri); + unsigned tid; + + if (!is_user_equal(rdata->msg_info.from, "transport_load_test")) + return PJ_FALSE; + + tid = (unsigned)pj_strtoul(&target->user); + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); + + if (rdata->msg_info.cseq->cseq != g_lt[tid].next_seq) { + PJ_LOG(1,(THIS_FILE, " err: expecting cseq %u, got %u", + g_lt[tid].next_seq, rdata->msg_info.cseq->cseq)); + g_lt[tid].err = PJ_TRUE; + g_lt[tid].next_seq = rdata->msg_info.cseq->cseq + 1; } else - mod_load.next_seq++; + g_lt[tid].next_seq++; return PJ_TRUE; } -int transport_load_test(char *target_url) +int transport_load_test(pjsip_transport_type_e tp_type, + const char *host_port_transport) { +#define ERR(rc__) { rc=rc__; goto on_return; } enum { COUNT = 2000 }; - unsigned i; - pj_status_t status = PJ_SUCCESS; + char target_url[64]; + unsigned i, tid=tp_type; + int rc; + + pj_ansi_snprintf(target_url, sizeof(target_url), "sip:%d@%s", + tp_type, host_port_transport); /* exhaust packets */ - do { - pj_time_val delay = {1, 0}; - i = 0; - pjsip_endpt_handle_events2(endpt, &delay, &i); - } while (i != 0); + flush_events(500); PJ_LOG(3,(THIS_FILE, " transport load test...")); - if (mod_load.mod.id == -1) { - status = pjsip_endpt_register_module( endpt, &mod_load.mod); - if (status != PJ_SUCCESS) { - app_perror("error registering module", status); - return -1; + pj_enter_critical_section(); + if (mod_load_test.id == -1) { + rc = pjsip_endpt_register_module( endpt, &mod_load_test); + if (rc != PJ_SUCCESS) { + pj_leave_critical_section(); + app_perror("error registering module", rc); + return -610; } } - mod_load.err = PJ_FALSE; - mod_load.next_seq = 0; + pj_leave_critical_section(); + g_lt[tid].err = PJ_FALSE; + g_lt[tid].next_seq = 0; - for (i=0; i"); + from = pj_str(""); call_id = pj_str("thecallid"); - status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, + PJ_TEST_SUCCESS(pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target, &from, &target, &from, &call_id, - i, NULL, &tdata ); - if (status != PJ_SUCCESS) { - app_perror("error creating request", status); - goto on_return; - } + i, NULL, &tdata ), + NULL, ERR(-620)); - status = pjsip_endpt_send_request_stateless(endpt, tdata, NULL, NULL); - if (status != PJ_SUCCESS) { - app_perror("error sending request", status); - goto on_return; - } - } + PJ_TEST_SUCCESS(pjsip_endpt_send_request_stateless(endpt, tdata, + NULL, NULL), + NULL, ERR(-630)); - do { - pj_time_val delay = {1, 0}; - i = 0; - pjsip_endpt_handle_events2(endpt, &delay, &i); - } while (i != 0); - - if (mod_load.next_seq != COUNT) { - PJ_LOG(1,("THIS_FILE", " err: expecting %u msg, got only %u", - COUNT, mod_load.next_seq)); - status = -2; - goto on_return; + flush_events(20); } + flush_events(1000); + + PJ_TEST_EQ(g_lt[tid].next_seq, COUNT, "message count mismatch", + ERR(-640)); + + rc = 0; + on_return: - if (mod_load.mod.id != -1) { - pjsip_endpt_unregister_module( endpt, &mod_load.mod); - mod_load.mod.id = -1; - } - if (status != PJ_SUCCESS || mod_load.err) { - return -2; + if (mod_load_test.id != -1) { + /* Don't unregister if there are multiple transport_load_test() */ + pjsip_endpt_unregister_module( endpt, &mod_load_test); + mod_load_test.id = -1; } - PJ_LOG(3,(THIS_FILE, " success")); - return 0; + return rc; } diff --git a/pjsip/src/test/transport_udp_test.c b/pjsip/src/test/transport_udp_test.c index 2c063d4846..958e954e15 100644 --- a/pjsip/src/test/transport_udp_test.c +++ b/pjsip/src/test/transport_udp_test.c @@ -23,87 +23,99 @@ #define THIS_FILE "transport_udp_test.c" -static pj_status_t multi_transport_test(pjsip_transport *tp[], unsigned num_tp) +static pj_status_t multi_transport_test(pjsip_transport *tp[], unsigned max_tp) { - pj_status_t status; - unsigned i = 0; +#define ERR(rc__) { rc=rc__; goto on_return; } + int rc; + unsigned i, num_tp=0; pj_str_t s; - pjsip_transport *udp_tp; + pjsip_transport *other_udp_tp = NULL, *udp_tp; pj_sockaddr_in rem_addr; pjsip_tpselector tp_sel; - for (;iref_cnt) != 1) - return -120; + PJ_TEST_EQ(pj_atomic_get(udp_tp->ref_cnt), 1, NULL, ERR(-120)); - /* Test basic transport attributes */ - status = generic_transport_test(udp_tp); - if (status != PJ_SUCCESS) - return status; - - tp[i] = udp_tp; + tp[num_tp++] = udp_tp; } for (i = 0; i < num_tp; ++i) { - udp_tp = tp[i]; - if (pj_atomic_get(udp_tp->ref_cnt) != 1) - return -130; + PJ_TEST_EQ(pj_atomic_get(tp[i]->ref_cnt), 1, NULL, ERR(-130)); + + /* Test basic transport attributes */ + rc = generic_transport_test(udp_tp); + if (rc != 0) + goto on_return; } /* Acquire transport test without selector. */ pj_sockaddr_in_init(&rem_addr, pj_cstr(&s, "1.1.1.1"), 80); - status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_UDP, + PJ_TEST_SUCCESS(pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_UDP, &rem_addr, sizeof(rem_addr), - NULL, &udp_tp); - if (status != PJ_SUCCESS) - return -140; + NULL, &udp_tp), + NULL, ERR(-140)); for (i = 0; i < num_tp; ++i) { if (udp_tp == tp[i]) { break; } } - if (i == num_tp) - return -150; + PJ_TEST_TRUE(iref_cnt) != 1) - return -160; + if (udp_tp == other_udp_tp) { + PJ_TEST_GT(pj_atomic_get(udp_tp->ref_cnt), 1, NULL, ERR(-160)); + } else { + PJ_TEST_EQ(pj_atomic_get(udp_tp->ref_cnt), 1, NULL, ERR(-165)); + } /* Acquire transport test with selector. */ pj_bzero(&tp_sel, sizeof(tp_sel)); tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; tp_sel.u.transport = tp[num_tp-1]; pj_sockaddr_in_init(&rem_addr, pj_cstr(&s, "1.1.1.1"), 80); - status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_UDP, + PJ_TEST_SUCCESS( pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_UDP, &rem_addr, sizeof(rem_addr), - &tp_sel, &udp_tp); - if (status != PJ_SUCCESS) - return -170; + &tp_sel, &udp_tp), + NULL, ERR(-170)); - if (udp_tp != tp[num_tp-1]) - return -180; + PJ_TEST_EQ(udp_tp, tp[num_tp-1], NULL, ERR(-180)); pjsip_transport_dec_ref(udp_tp); - if (pj_atomic_get(udp_tp->ref_cnt) != 1) - return -190; + PJ_TEST_EQ(pj_atomic_get(udp_tp->ref_cnt), 1, NULL, ERR(-190)); + + rc = 0; + +on_return: + if (other_udp_tp) { + pjsip_transport_dec_ref(other_udp_tp); + } + if (rc != 0) { + for (i=0; ipool, &tsx_key, &tsx->transaction_key); found = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_FALSE); @@ -72,7 +86,7 @@ static int tsx_layer_test(void) } /* Double terminate test. */ -static int double_terminate(void) +static int double_terminate(unsigned tid) { pj_str_t target, from, tsx_key; pjsip_tx_data *tdata; @@ -81,8 +95,8 @@ static int double_terminate(void) PJ_LOG(3,(THIS_FILE, " double terminate test")); - target = pj_str(TARGET_URI); - from = pj_str(FROM_URI); + target = pj_str(g[tid].TARGET_URI); + from = pj_str(g[tid].FROM_URI); /* Create request. */ status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target, @@ -100,6 +114,16 @@ static int double_terminate(void) return -20; } + if (g[tid].tp) { + pjsip_tpselector tp_sel; + + pj_bzero(&tp_sel, sizeof(tp_sel)); + tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; + tp_sel.u.transport = g[tid].tp; + + pjsip_tsx_set_transport(tsx, &tp_sel); + } + /* Save transaction key for later. */ pj_strdup_with_null(tdata->pool, &tsx_key, &tsx->transaction_key); @@ -135,26 +159,44 @@ static int double_terminate(void) return PJ_SUCCESS; } -int tsx_basic_test(struct tsx_test_param *param) +int tsx_basic_test(unsigned tid) { + struct tsx_test_param *param = &tsx_test[tid]; + pjsip_transport *loop = NULL; int status; - pj_ansi_snprintf(TARGET_URI, sizeof(TARGET_URI), - "sip:bob@127.0.0.1:%d;transport=%s", + g[tid].tp = NULL; + + if (param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + PJ_TEST_SUCCESS(pjsip_loop_start(endpt, &loop), NULL, return -10); + pjsip_transport_add_ref(loop); + g[tid].tp = loop; + } + pj_ansi_snprintf(g[tid].TARGET_URI, sizeof(g[tid].TARGET_URI), + "sip:tsx_basic_test@127.0.0.1:%d;transport=%s", param->port, param->tp_type); - pj_ansi_snprintf(FROM_URI, sizeof(FROM_URI), - "sip:alice@127.0.0.1:%d;transport=%s", + pj_ansi_snprintf(g[tid].FROM_URI, sizeof(g[tid].FROM_URI), + "sip:tsx_basic_test@127.0.0.1:%d;transport=%s", param->port, param->tp_type); - status = tsx_layer_test(); + status = tsx_layer_test(tid); if (status != 0) - return status; + goto on_return; - status = double_terminate(); + status = double_terminate(tid); if (status != 0) - return status; + goto on_return; - return 0; + status = 0; + +on_return: + if (loop) { + /* Order must be shutdown then dec_ref so it gets destroyed */ + pjsip_transport_shutdown(loop); + pjsip_transport_dec_ref(loop); + flush_events(500); + } + return status; } /**************************************************************************/ diff --git a/pjsip/src/test/tsx_bench.c b/pjsip/src/test/tsx_bench.c index 30b2aa2209..8c0d8bf85b 100644 --- a/pjsip/src/test/tsx_bench.c +++ b/pjsip/src/test/tsx_bench.c @@ -20,7 +20,7 @@ #include #include -#define THIS_FILE "tsx_uas_test.c" +#define THIS_FILE "tsx_bench.c" static pjsip_module mod_tsx_user; @@ -32,22 +32,19 @@ static int uac_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) pjsip_transaction **tsx; pj_timestamp t1, t2, elapsed; pjsip_via_hdr *via; - pj_status_t status; + int rc; /* Create the request first. */ pj_str_t str_target = pj_str("sip:someuser@someprovider.com"); - pj_str_t str_from = pj_str("\"Local User\" "); + pj_str_t str_from = pj_str("\"Local User\" "); pj_str_t str_to = pj_str("\"Remote User\" "); pj_str_t str_contact = str_from; - status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, + PJ_TEST_SUCCESS(pjsip_endpt_create_request(endpt, &pjsip_invite_method, &str_target, &str_from, &str_to, &str_contact, NULL, -1, NULL, - &request); - if (status != PJ_SUCCESS) { - app_perror(" error: unable to create request", status); - return status; - } + &request), + NULL, return -110); via = (pjsip_via_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_VIA, NULL); @@ -62,9 +59,9 @@ static int uac_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) elapsed.u64 = 0; pj_get_timestamp(&t1); for (i=0; ibranch_param.slen = 0; } @@ -73,7 +70,7 @@ static int uac_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) pj_add_timestamp(&elapsed, &t2); p_elapsed->u64 = elapsed.u64; - status = PJ_SUCCESS; + rc = 0; on_error: for (i=0; i"); + pj_str_t str_from = pj_str("\"Local User\" "); pj_str_t str_to = pj_str("\"Remote User\" "); pj_str_t str_contact = str_from; - status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, + PJ_TEST_SUCCESS( pjsip_endpt_create_request(endpt, &pjsip_invite_method, &str_target, &str_from, &str_to, &str_contact, NULL, -1, NULL, - &request); - if (status != PJ_SUCCESS) { - app_perror(" error: unable to create request", status); - return status; - } + &request), + NULL, return -210); /* Create Via */ via = pjsip_via_hdr_create(request->pool); @@ -141,15 +135,10 @@ static int uas_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) rdata.msg_info.cid = (pjsip_cid_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_CALL_ID, NULL); rdata.msg_info.via = via; - pj_sockaddr_in_init(&remote, 0, 0); - status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM, - &remote, sizeof(pj_sockaddr_in), - NULL, &rdata.tp_info.transport); - if (status != PJ_SUCCESS) { - app_perror(" error: unable to get loop transport", status); - return status; - } - + PJ_TEST_SUCCESS(pjsip_loop_start(endpt, &loop), NULL, + { pjsip_tx_data_dec_ref(request); return -220; }); + pjsip_transport_add_ref(loop); + rdata.tp_info.transport = loop; /* Create transaction array */ tsx = (pjsip_transaction**) pj_pool_zalloc(request->pool, working_set * sizeof(pjsip_transaction*)); @@ -167,17 +156,15 @@ static int uas_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) pj_ansi_snprintf(branch_buf+PJSIP_RFC3261_BRANCH_LEN, sizeof(branch_buf)-PJSIP_RFC3261_BRANCH_LEN, "-%d", i); - status = pjsip_tsx_create_uas(&mod_tsx_user, &rdata, &tsx[i]); - if (status != PJ_SUCCESS) - goto on_error; - + PJ_TEST_SUCCESS(pjsip_tsx_create_uas(&mod_tsx_user, &rdata, &tsx[i]), + NULL, { rc=-230; goto on_error; }); } pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&elapsed, &t2); p_elapsed->u64 = elapsed.u64; - status = PJ_SUCCESS; + rc = 0; on_error: for (i=0; itp_type is loop. */ + pjsip_transport *loop; + + struct my_timer timer; + +} g[MAX_TSX_TESTS]; static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e); static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata); @@ -118,13 +142,17 @@ static pjsip_module tsx_user = &tsx_user_on_tsx_state, /* on_tsx_state() */ }; -/* Module to receive the loop-backed request. */ +/* Module to receive the loop-backed request and also process tx msgs. */ static pjsip_module msg_receiver = { NULL, NULL, /* prev and next */ { "Msg-Receiver", 12}, /* Name. */ -1, /* Id */ - PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */ + /* Note: + * Priority needs to be more important than UA layer, because UA layer + * silently absorbs ACK with To tag. + */ + PJSIP_MOD_PRIORITY_UA_PROXY_LAYER-1,/* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ @@ -136,21 +164,59 @@ static pjsip_module msg_receiver = NULL, /* on_tsx_state() */ }; -/* Static vars, which will be reset on each test. */ -static int recv_count; -static pj_time_val recv_last; -static pj_bool_t test_complete; +/* Init uac test */ +static int init_test(unsigned tid) +{ + pj_enter_critical_section(); + if (tsx_user.id == -1) { + PJ_TEST_SUCCESS(pjsip_endpt_register_module(endpt, &tsx_user), NULL, + { pj_leave_critical_section(); return -30; }); + } -/* Loop transport instance. */ -static pjsip_transport *loop; + if (msg_receiver.id == -1) { + PJ_TEST_SUCCESS(pjsip_endpt_register_module(endpt, &msg_receiver), NULL, + { pj_leave_critical_section(); return -40; }); + } + pj_leave_critical_section(); + + pj_assert(g[tid].loop==NULL); + + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + PJ_TEST_SUCCESS(pjsip_loop_start(endpt, &g[tid].loop), + NULL, return -50); + pjsip_transport_add_ref(g[tid].loop); + } + return PJ_SUCCESS; +} -/* General timer entry to be used by tests. */ -static struct my_timer +/* Finish test */ +static void finish_test(unsigned tid) { - pj_timer_entry entry; - char key_buf[1024]; - pj_str_t tsx_key; -} timer; + /* Note: don't unregister modules on_tsx_state() may be called when + * transaction layer is shutdown later, which will cause + * get_tsx_tid() to be called and it needs the module id. + */ + if (g[tid].loop) { + /* Order must be shutdown then dec_ref so it gets destroyed */ + pjsip_transport_shutdown(g[tid].loop); + pjsip_transport_dec_ref(g[tid].loop); + g[tid].loop = 0; + } +} + +/* Get test ID from transaction instance */ +static unsigned get_tsx_tid(const pjsip_transaction *tsx) +{ + pj_assert(tsx_user.id >= 0); + return (unsigned)(long)tsx->mod_data[tsx_user.id]; +} + +/* Set test ID to transaction instance */ +static void set_tsx_tid(pjsip_transaction *tsx, unsigned tid) +{ + pj_assert(tsx_user.id >= 0); + tsx->mod_data[tsx_user.id] = (void*)(long)tid; +} /* * This is the handler to receive state changed notification from the @@ -159,34 +225,63 @@ static struct my_timer */ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { - if (pj_stricmp2(&tsx->branch, TEST1_BRANCH_ID)==0) { + unsigned tid = get_tsx_tid(tsx); + + /* + PJ_LOG(3,(THIS_FILE, + " on_tsx_state state: %s, event: %s (%s)", + pjsip_tsx_state_str(tsx->state), + pjsip_event_str(e->type), + pjsip_event_str(e->body.tsx_state.type) + )); + */ + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM && + e->type==PJSIP_EVENT_TSX_STATE) + { + if (e->body.tsx_state.type==PJSIP_EVENT_RX_MSG) { + PJ_TEST_EQ( e->body.tsx_state.src.rdata->tp_info.transport, + g[tid].loop, NULL, { + g[tid].test_complete = -704; + return; + }); + } else if (e->body.tsx_state.type==PJSIP_EVENT_TX_MSG && + e->body.tsx_state.src.tdata->dest_info.addr.count) { + PJ_TEST_EQ( e->body.tsx_state.src.tdata->tp_info.transport, + g[tid].loop, NULL, { + g[tid].test_complete = -706; + return; + }); + } + } + + if (pj_strnicmp2(&tsx->branch, TEST1_BRANCH_ID, BRANCH_LEN)==0) { /* * Transaction with TEST1_BRANCH_ID should terminate with transaction * timeout status. */ if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - if (test_complete == 0) - test_complete = 1; + if (g[tid].test_complete == 0) + g[tid].test_complete = 1; /* Test the status code. */ if (tsx->status_code != PJSIP_SC_TSX_TIMEOUT) { PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, PJSIP_SC_TSX_TIMEOUT)); - test_complete = -710; + g[tid].test_complete = -710; } /* If transport is reliable, then there must not be any * retransmissions. */ - if (tp_flag & PJSIP_TRANSPORT_RELIABLE) { - if (recv_count != 1) { + if (g[tid].tp_flag & PJSIP_TRANSPORT_RELIABLE) { + if (g[tid].recv_count != 1) { PJ_LOG(3,(THIS_FILE, " error: there were %d (re)transmissions", - recv_count)); - test_complete = -715; + g[tid].recv_count)); + g[tid].test_complete = -715; } } else { /* Check the number of (re)transmissions, which must be @@ -197,29 +292,29 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) * fires first which causes recv_count fall short (by one). */ //if (tsx->method.id==PJSIP_INVITE_METHOD && recv_count != 7) { - if (tsx->method.id==PJSIP_INVITE_METHOD && recv_count < 6) { + if (tsx->method.id==PJSIP_INVITE_METHOD && g[tid].recv_count < 6) { PJ_LOG(3,(THIS_FILE, " error: there were %d (re)transmissions", - recv_count)); - test_complete = -716; + g[tid].recv_count)); + g[tid].test_complete = -716; } else //if (tsx->method.id==PJSIP_OPTIONS_METHOD && recv_count != 11) { - if (tsx->method.id==PJSIP_OPTIONS_METHOD && recv_count < 10) { + if (tsx->method.id==PJSIP_OPTIONS_METHOD && g[tid].recv_count < 10) { PJ_LOG(3,(THIS_FILE, " error: there were %d (re)transmissions", - recv_count)); - test_complete = -717; + g[tid].recv_count)); + g[tid].test_complete = -717; } else if (tsx->method.id!=PJSIP_INVITE_METHOD && tsx->method.id!=PJSIP_OPTIONS_METHOD) { PJ_LOG(3,(THIS_FILE, " error: unexpected method")); - test_complete = -718; + g[tid].test_complete = -718; } } } - } else if (pj_stricmp2(&tsx->branch, TEST2_BRANCH_ID)==0) { + } else if (pj_strnicmp2(&tsx->branch, TEST2_BRANCH_ID, BRANCH_LEN)==0) { /* * Transaction with TEST2_BRANCH_ID should terminate with transport error. */ @@ -233,14 +328,14 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) " error: status code is %d instead of %d or %d", tsx->status_code, PJSIP_SC_TSX_TRANSPORT_ERROR, PJSIP_SC_BAD_GATEWAY)); - test_complete = -720; + g[tid].test_complete = -720; } - if (test_complete == 0) - test_complete = 1; + if (g[tid].test_complete == 0) + g[tid].test_complete = 1; } - } else if (pj_stricmp2(&tsx->branch, TEST3_BRANCH_ID)==0) { + } else if (pj_strnicmp2(&tsx->branch, TEST3_BRANCH_ID, BRANCH_LEN)==0) { /* * This test terminates the transaction while resolver is still * running. @@ -257,15 +352,15 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, PJSIP_SC_REQUEST_TERMINATED)); - test_complete = -730; + g[tid].test_complete = -730; } - if (test_complete == 0) - test_complete = 1; + if (g[tid].test_complete == 0) + g[tid].test_complete = 1; } - } else if (pj_stricmp2(&tsx->branch, TEST4_BRANCH_ID)==0) { + } else if (pj_strnicmp2(&tsx->branch, TEST4_BRANCH_ID, BRANCH_LEN)==0) { /* * This test simulates transport failure after several * retransmissions. @@ -277,7 +372,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, PJSIP_SC_TSX_TRANSPORT_ERROR)); - test_complete = -730; + g[tid].test_complete = -730; } /* Must have correct retransmission count. */ @@ -285,15 +380,15 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: retransmit cnt is %d instead of %d", tsx->retransmit_count, TEST4_RETRANSMIT_CNT)); - test_complete = -731; + g[tid].test_complete = -731; } - if (test_complete == 0) - test_complete = 1; + if (g[tid].test_complete == 0) + g[tid].test_complete = 1; } - } else if (pj_stricmp2(&tsx->branch, TEST5_BRANCH_ID)==0) { + } else if (pj_strnicmp2(&tsx->branch, TEST5_BRANCH_ID, BRANCH_LEN)==0) { /* * This test simulates transport failure after several * retransmissions. @@ -305,7 +400,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, PJSIP_SC_REQUEST_TERMINATED)); - test_complete = -733; + g[tid].test_complete = -733; } /* Must have correct retransmission count. */ @@ -313,15 +408,15 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: retransmit cnt is %d instead of %d", tsx->retransmit_count, TEST5_RETRANSMIT_CNT)); - test_complete = -734; + g[tid].test_complete = -734; } - if (test_complete == 0) - test_complete = 1; + if (g[tid].test_complete == 0) + g[tid].test_complete = 1; } - } else if (pj_stricmp2(&tsx->branch, TEST6_BRANCH_ID)==0) { + } else if (pj_strnicmp2(&tsx->branch, TEST6_BRANCH_ID, BRANCH_LEN)==0) { /* * Successfull non-INVITE transaction. */ @@ -332,7 +427,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, 202)); - test_complete = -736; + g[tid].test_complete = -736; } /* Must have correct retransmission count. */ @@ -340,18 +435,18 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: retransmit cnt is %d instead of %d", tsx->retransmit_count, 0)); - test_complete = -737; + g[tid].test_complete = -737; } /* Must still keep last_tx */ if (tsx->last_tx == NULL) { PJ_LOG(3,(THIS_FILE, " error: transaction lost last_tx")); - test_complete = -738; + g[tid].test_complete = -738; } - if (test_complete == 0) { - test_complete = 1; + if (g[tid].test_complete == 0) { + g[tid].test_complete = 1; pjsip_tsx_terminate(tsx, 202); } @@ -359,12 +454,12 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Previous state must be COMPLETED. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { - test_complete = -7381; + g[tid].test_complete = -7381; } } - } else if (pj_stricmp2(&tsx->branch, TEST7_BRANCH_ID)==0) { + } else if (pj_strnicmp2(&tsx->branch, TEST7_BRANCH_ID, BRANCH_LEN)==0) { /* * Successfull non-INVITE transaction. */ @@ -376,7 +471,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) " error: prev state is %s instead of %s", pjsip_tsx_state_str((pjsip_tsx_state_e)e->body.tsx_state.prev_state), pjsip_tsx_state_str(PJSIP_TSX_STATE_PROCEEDING))); - test_complete = -739; + g[tid].test_complete = -739; } /* Status code must be 202. */ @@ -384,7 +479,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, 202)); - test_complete = -740; + g[tid].test_complete = -740; } /* Must have correct retransmission count. */ @@ -392,18 +487,18 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: retransmit cnt is %d instead of %d", tsx->retransmit_count, 0)); - test_complete = -741; + g[tid].test_complete = -741; } /* Must still keep last_tx */ if (tsx->last_tx == NULL) { PJ_LOG(3,(THIS_FILE, " error: transaction lost last_tx")); - test_complete = -741; + g[tid].test_complete = -741; } - if (test_complete == 0) { - test_complete = 1; + if (g[tid].test_complete == 0) { + g[tid].test_complete = 1; pjsip_tsx_terminate(tsx, 202); } @@ -411,13 +506,13 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Previous state must be COMPLETED. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { - test_complete = -742; + g[tid].test_complete = -742; } } - } else if (pj_stricmp2(&tsx->branch, TEST8_BRANCH_ID)==0) { + } else if (pj_strnicmp2(&tsx->branch, TEST8_BRANCH_ID, BRANCH_LEN)==0) { /* * Failed INVITE transaction. */ @@ -428,7 +523,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, 301)); - test_complete = -745; + g[tid].test_complete = -745; } /* Must have correct retransmission count. */ @@ -436,14 +531,14 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: retransmit cnt is %d instead of %d", tsx->retransmit_count, 0)); - test_complete = -746; + g[tid].test_complete = -746; } /* Must still keep last_tx */ if (tsx->last_tx == NULL) { PJ_LOG(3,(THIS_FILE, " error: transaction lost last_tx")); - test_complete = -747; + g[tid].test_complete = -747; } /* last_tx MUST be the INVITE request @@ -454,16 +549,19 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { PJ_LOG(3,(THIS_FILE, " error: last_tx is not INVITE")); - test_complete = -748; + g[tid].test_complete = -748; } } else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - test_complete = 1; + g[tid].test_complete = 1; /* Previous state must be COMPLETED. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { - test_complete = -750; + PJ_LOG(3,(THIS_FILE, + " error: expecting last state=COMLETED instead of %d", + e->body.tsx_state.prev_state)); + g[tid].test_complete = -750; } /* Status code must be 301. */ @@ -471,13 +569,13 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, 301)); - test_complete = -751; + g[tid].test_complete = -751; } } - } else if (pj_stricmp2(&tsx->branch, TEST9_BRANCH_ID)==0) { + } else if (pj_strnicmp2(&tsx->branch, TEST9_BRANCH_ID, BRANCH_LEN)==0) { /* * Failed INVITE transaction with provisional response. */ @@ -485,7 +583,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Previous state must be PJSIP_TSX_STATE_PROCEEDING. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_PROCEEDING) { - test_complete = -760; + g[tid].test_complete = -760; } /* Status code must be 302. */ @@ -493,7 +591,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, 302)); - test_complete = -761; + g[tid].test_complete = -761; } /* Must have correct retransmission count. */ @@ -501,14 +599,14 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: retransmit cnt is %d instead of %d", tsx->retransmit_count, 0)); - test_complete = -762; + g[tid].test_complete = -762; } /* Must still keep last_tx */ if (tsx->last_tx == NULL) { PJ_LOG(3,(THIS_FILE, " error: transaction lost last_tx")); - test_complete = -763; + g[tid].test_complete = -763; } /* last_tx MUST be INVITE. @@ -519,17 +617,17 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { PJ_LOG(3,(THIS_FILE, " error: last_tx is not INVITE")); - test_complete = -764; + g[tid].test_complete = -764; } } else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - test_complete = 1; + g[tid].test_complete = 1; /* Previous state must be COMPLETED. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { - test_complete = -767; + g[tid].test_complete = -767; } /* Status code must be 302. */ @@ -537,7 +635,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, 302)); - test_complete = -768; + g[tid].test_complete = -768; } } @@ -594,7 +692,34 @@ static void terminate_tsx_callback( pj_timer_heap_t *timer_heap, */ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) { - if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST1_BRANCH_ID) == 0) { + pjsip_to_hdr *to_hdr = rdata->msg_info.to; + pjsip_sip_uri *target = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri); + pjsip_to_hdr *from_hdr = rdata->msg_info.from; + pjsip_sip_uri *from_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(from_hdr->uri); + unsigned tid; + + if (pj_strcmp2(&from_uri->user, "tsx_uac_test")) { + /* Not our message */ + return PJ_FALSE; + } + + tid = (unsigned)pj_strtoul(&target->user); + + /* + PJ_LOG(3,(THIS_FILE, " on_rx_request %s (recv_count: %d) on %s, branch: %.*s", + pjsip_rx_data_get_info(rdata), + g[tid].recv_count, rdata->tp_info.transport->info, + (int)rdata->msg_info.via->branch_param.slen, + rdata->msg_info.via->branch_param.ptr)); + */ + + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + PJ_TEST_EQ(rdata->tp_info.transport, g[tid].loop, NULL, + {g[tid].test_complete = -602; return PJ_TRUE;}); + } + + if (pj_strnicmp2(&rdata->msg_info.via->branch_param, TEST1_BRANCH_ID, + BRANCH_LEN) == 0) { /* * The TEST1_BRANCH_ID test performs the verifications for transaction * retransmission mechanism. It will not answer the incoming request @@ -611,14 +736,14 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) PJ_LOG(3,(THIS_FILE, " error: received unexpected method %.*s", (int)msg->line.req.method.name.slen, msg->line.req.method.name.ptr)); - test_complete = -600; + g[tid].test_complete = -600; return PJ_TRUE; } - if (recv_count == 0) { - recv_count++; + if (g[tid].recv_count == 0) { + g[tid].recv_count++; //pj_gettimeofday(&recv_last); - recv_last = rdata->pkt_info.timestamp; + g[tid].recv_last = rdata->pkt_info.timestamp; } else { pj_time_val now; unsigned msec_expected, msec_elapsed; @@ -626,11 +751,11 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) //pj_gettimeofday(&now); now = rdata->pkt_info.timestamp; - PJ_TIME_VAL_SUB(now, recv_last); + PJ_TIME_VAL_SUB(now, g[tid].recv_last); msec_elapsed = now.sec*1000 + now.msec; - ++recv_count; - msec_expected = (1<<(recv_count-2))*pjsip_cfg()->tsx.t1; + ++g[tid].recv_count; + msec_expected = (1<<(g[tid].recv_count-2))*pjsip_cfg()->tsx.t1; if (msg->line.req.method.id != PJSIP_INVITE_METHOD) { if (msec_expected > pjsip_cfg()->tsx.t2) @@ -644,53 +769,55 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) PJ_LOG(3,(THIS_FILE, " error: expecting retransmission no. %d in %d " "ms, received in %d ms", - recv_count-1, msec_expected, msec_elapsed)); - test_complete = -610; + g[tid].recv_count-1, msec_expected, msec_elapsed)); + g[tid].test_complete = -610; } - if (recv_count > max_received) { + if (g[tid].recv_count > max_received) { PJ_LOG(3,(THIS_FILE, " error: too many messages (%d) received", - recv_count)); - test_complete = -620; + g[tid].recv_count)); + g[tid].test_complete = -620; } //pj_gettimeofday(&recv_last); - recv_last = rdata->pkt_info.timestamp; + g[tid].recv_last = rdata->pkt_info.timestamp; } return PJ_TRUE; } else - if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST4_BRANCH_ID) == 0) { + if (pj_strnicmp2(&rdata->msg_info.via->branch_param, TEST4_BRANCH_ID, + BRANCH_LEN) == 0) { /* * The TEST4_BRANCH_ID test simulates transport failure after several * retransmissions. */ - recv_count++; + g[tid].recv_count++; - if (recv_count == TEST4_RETRANSMIT_CNT) { + if (g[tid].recv_count == TEST4_RETRANSMIT_CNT) { /* Simulate transport failure. */ - pjsip_loop_set_failure(loop, 2, NULL); + pjsip_loop_set_failure(g[tid].loop, 2, NULL); - } else if (recv_count > TEST4_RETRANSMIT_CNT) { + } else if (g[tid].recv_count > TEST4_RETRANSMIT_CNT) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", - recv_count)); - test_complete = -631; + g[tid].recv_count)); + g[tid].test_complete = -631; } return PJ_TRUE; } else - if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST5_BRANCH_ID) == 0) { + if (pj_strnicmp2(&rdata->msg_info.via->branch_param, TEST5_BRANCH_ID, + BRANCH_LEN) == 0) { /* * The TEST5_BRANCH_ID test simulates user terminating the transaction * after several retransmissions. */ - recv_count++; + g[tid].recv_count++; - if (recv_count == TEST5_RETRANSMIT_CNT+1) { + if (g[tid].recv_count == TEST5_RETRANSMIT_CNT+1) { pj_str_t key; pjsip_transaction *tsx; @@ -702,44 +829,46 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) pj_grp_lock_release(tsx->grp_lock); } else { PJ_LOG(3,(THIS_FILE, " error: uac transaction not found!")); - test_complete = -633; + g[tid].test_complete = -633; } - } else if (recv_count > TEST5_RETRANSMIT_CNT+1) { + } else if (g[tid].recv_count > TEST5_RETRANSMIT_CNT+1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", - recv_count)); - test_complete = -634; + g[tid].recv_count)); + g[tid].test_complete = -634; } return PJ_TRUE; } else - if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST6_BRANCH_ID) == 0) { + if (pj_strnicmp2(&rdata->msg_info.via->branch_param, TEST6_BRANCH_ID, + BRANCH_LEN) == 0) { /* * The TEST6_BRANCH_ID test successfull non-INVITE transaction. */ pj_status_t status; - recv_count++; + g[tid].recv_count++; - if (recv_count > 1) { + if (g[tid].recv_count > 1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", - recv_count)); - test_complete = -635; + g[tid].recv_count)); + g[tid].test_complete = -635; } status = pjsip_endpt_respond_stateless(endpt, rdata, 202, NULL, NULL, NULL); if (status != PJ_SUCCESS) { app_perror(" error: unable to send response", status); - test_complete = -636; + g[tid].test_complete = -636; } return PJ_TRUE; } else - if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST7_BRANCH_ID) == 0) { + if (pj_strnicmp2(&rdata->msg_info.via->branch_param, TEST7_BRANCH_ID, + BRANCH_LEN) == 0) { /* * The TEST7_BRANCH_ID test successfull non-INVITE transaction * with provisional response. @@ -750,12 +879,12 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) pjsip_tx_data *tdata; pj_time_val delay = { 2, 0 }; - recv_count++; + g[tid].recv_count++; - if (recv_count > 1) { + if (g[tid].recv_count > 1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", - recv_count)); - test_complete = -640; + g[tid].recv_count)); + g[tid].test_complete = -640; return PJ_TRUE; } @@ -781,42 +910,43 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) if (r->res_addr.transport) pjsip_transport_add_ref(r->res_addr.transport); - timer.entry.cb = &send_response_callback; - timer.entry.user_data = r; - pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay); + g[tid].timer.entry.cb = &send_response_callback; + g[tid].timer.entry.user_data = r; + pjsip_endpt_schedule_timer(endpt, &g[tid].timer.entry, &delay); return (status == PJ_SUCCESS); } else - if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST8_BRANCH_ID) == 0) { + if (pj_strnicmp2(&rdata->msg_info.via->branch_param, TEST8_BRANCH_ID, + BRANCH_LEN) == 0) { /* * The TEST8_BRANCH_ID test failed INVITE transaction. */ pjsip_method *method; pj_status_t status; + method = &rdata->msg_info.msg->line.req.method; - - recv_count++; + g[tid].recv_count++; if (method->id == PJSIP_INVITE_METHOD) { - if (recv_count > 1) { + if (g[tid].recv_count > 1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", - recv_count)); - test_complete = -635; + g[tid].recv_count)); + g[tid].test_complete = -635; } status = pjsip_endpt_respond_stateless(endpt, rdata, 301, NULL, NULL, NULL); if (status != PJ_SUCCESS) { app_perror(" error: unable to send response", status); - test_complete = -636; + g[tid].test_complete = -636; } } else if (method->id == PJSIP_ACK_METHOD) { - if (recv_count == 2) { + if (g[tid].recv_count == 2) { pj_str_t key; pj_time_val delay = { 5, 0 }; @@ -828,30 +958,31 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) PJSIP_ROLE_UAC, &pjsip_invite_method, rdata); - pj_strcpy(&timer.tsx_key, &key); - timer.entry.id = 301; - timer.entry.cb = &terminate_tsx_callback; + pj_strcpy(&g[tid].timer.tsx_key, &key); + g[tid].timer.entry.id = 301; + g[tid].timer.entry.cb = &terminate_tsx_callback; - pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay); + pjsip_endpt_schedule_timer(endpt, &g[tid].timer.entry, &delay); } - if (recv_count > 2) { + if (g[tid].recv_count > 2) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", - recv_count)); - test_complete = -638; + g[tid].recv_count)); + g[tid].test_complete = -638; } } else { PJ_LOG(3,(THIS_FILE," error: not expecting %s", pjsip_rx_data_get_info(rdata))); - test_complete = -639; + g[tid].test_complete = -639; } } else - if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST9_BRANCH_ID) == 0) { + if (pj_strnicmp2(&rdata->msg_info.via->branch_param, TEST9_BRANCH_ID, + BRANCH_LEN) == 0) { /* * The TEST9_BRANCH_ID test failed INVITE transaction with * provisional response. @@ -861,7 +992,7 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) method = &rdata->msg_info.msg->line.req.method; - recv_count++; + g[tid].recv_count++; if (method->id == PJSIP_INVITE_METHOD) { @@ -870,10 +1001,10 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) pjsip_tx_data *tdata; pj_time_val delay = { 2, 0 }; - if (recv_count > 1) { + if (g[tid].recv_count > 1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", - recv_count)); - test_complete = -650; + g[tid].recv_count)); + g[tid].test_complete = -650; return PJ_TRUE; } @@ -901,13 +1032,13 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) if (r->res_addr.transport) pjsip_transport_add_ref(r->res_addr.transport); - timer.entry.cb = &send_response_callback; - timer.entry.user_data = r; - pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay); + g[tid].timer.entry.cb = &send_response_callback; + g[tid].timer.entry.user_data = r; + pjsip_endpt_schedule_timer(endpt, &g[tid].timer.entry, &delay); } else if (method->id == PJSIP_ACK_METHOD) { - if (recv_count == 2) { + if (g[tid].recv_count == 2) { pj_str_t key; pj_time_val delay = { 5, 0 }; @@ -919,24 +1050,24 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) PJSIP_ROLE_UAC, &pjsip_invite_method, rdata); - pj_strcpy(&timer.tsx_key, &key); - timer.entry.id = 302; - timer.entry.cb = &terminate_tsx_callback; + pj_strcpy(&g[tid].timer.tsx_key, &key); + g[tid].timer.entry.id = 302; + g[tid].timer.entry.cb = &terminate_tsx_callback; - pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay); + pjsip_endpt_schedule_timer(endpt, &g[tid].timer.entry, &delay); } - if (recv_count > 2) { + if (g[tid].recv_count > 2) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", - recv_count)); - test_complete = -638; + g[tid].recv_count)); + g[tid].test_complete = -638; } } else { PJ_LOG(3,(THIS_FILE," error: not expecting %s", pjsip_rx_data_get_info(rdata))); - test_complete = -639; + g[tid].test_complete = -639; } @@ -950,26 +1081,28 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) /* * The generic test framework, used by most of the tests. */ -static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, - char *branch_param, int test_time, +static int perform_tsx_test(unsigned tid, int dummy, char *target_uri, + char *from_uri, char *branch_param, int test_time, const pjsip_method *method) { pjsip_tx_data *tdata; pjsip_transaction *tsx; pj_str_t target, from, tsx_key; pjsip_via_hdr *via; + char branch_buf[BRANCH_LEN+20]; pj_time_val timeout; pj_status_t status; PJ_UNUSED_ARG(dummy); + PJ_TEST_EQ(strlen(branch_param), BRANCH_LEN, NULL, return -99); PJ_LOG(3,(THIS_FILE, " please standby, this will take at most %d seconds..", test_time)); /* Reset test. */ - recv_count = 0; - test_complete = 0; + g[tid].recv_count = 0; + g[tid].test_complete = 0; /* Init headers. */ target = pj_str(target_uri); @@ -981,12 +1114,16 @@ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, NULL, &tdata); if (status != PJ_SUCCESS) { app_perror(" Error: unable to create request", status); - return -100; + return -105; } - /* Set the branch param for test 1. */ + /* Set the branch param. Note that other tsx_uac_test() instances may + * be running simultaneously, thus the branch ID needs to be made unique + * by adding tid */ via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); - via->branch_param = pj_str(branch_param); + pj_ansi_snprintf(branch_buf, sizeof(branch_buf), + "%s-%02d", branch_param, tid); + pj_strdup2(tdata->pool, &via->branch_param, branch_buf); /* Add additional reference to tdata to prevent transaction from * deleting it. @@ -1000,7 +1137,18 @@ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, pjsip_tx_data_dec_ref(tdata); return -110; } + set_tsx_tid(tsx, tid); + + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_tpselector tp_sel; + pj_assert(g[tid].loop); + pj_bzero(&tp_sel, sizeof(tp_sel)); + tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; + tp_sel.u.transport = g[tid].loop; + pjsip_tsx_set_transport(tsx, &tp_sel); + } + /* Get transaction key. */ pj_strdup(tdata->pool, &tsx_key, &tsx->transaction_key); @@ -1020,7 +1168,7 @@ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, timeout.sec += test_time; /* Wait until test complete. */ - while (!test_complete) { + while (!g[tid].test_complete) { pj_time_val now, poll_delay = {0, 10}; pjsip_endpt_handle_events(endpt, &poll_delay); @@ -1033,7 +1181,7 @@ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, } } - if (test_complete < 0) { + if (g[tid].test_complete < 0) { tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); if (tsx) { pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED); @@ -1041,7 +1189,7 @@ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, flush_events(1000); } pjsip_tx_data_dec_ref(tdata); - return test_complete; + return g[tid].test_complete; } else { pj_time_val now; @@ -1068,10 +1216,11 @@ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, } /* Check tdata reference counter. */ - if (pj_atomic_get(tdata->ref_cnt) != 1) { + if (pj_atomic_get(tdata->ref_cnt) > 1) { PJ_LOG(3,(THIS_FILE, " Error: tdata reference counter is %ld", pj_atomic_get(tdata->ref_cnt))); - pjsip_tx_data_dec_ref(tdata); + while (pj_atomic_get(tdata->ref_cnt) > 1) + pjsip_tx_data_dec_ref(tdata); return -150; } @@ -1091,7 +1240,7 @@ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, ** ***************************************************************************** */ -static int tsx_uac_retransmit_test(void) +static int tsx_uac_retransmit_test(unsigned tid) { int status = 0, enabled; int i; @@ -1111,7 +1260,6 @@ static int tsx_uac_retransmit_test(void) PJ_LOG(3,(THIS_FILE, " test1: basic uac retransmit and timeout test")); - /* For this test. message printing shound be disabled because it makes * incorrect timing. */ @@ -1126,19 +1274,23 @@ static int tsx_uac_retransmit_test(void) sub_test[i].delay)); /* Configure transport */ - pjsip_loop_set_failure(loop, 0, NULL); - pjsip_loop_set_recv_delay(loop, sub_test[i].delay, NULL); + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_loop_set_failure(g[tid].loop, 0, NULL); + pjsip_loop_set_recv_delay(g[tid].loop, sub_test[i].delay, NULL); + } /* Do the test. */ - status = perform_tsx_test(-500, TARGET_URI, FROM_URI, - TEST1_BRANCH_ID, + status = perform_tsx_test(tid, -500, g[tid].TARGET_URI, + g[tid].FROM_URI, TEST1_BRANCH_ID, 35, sub_test[i].method); if (status != 0) break; } /* Restore transport. */ - pjsip_loop_set_recv_delay(loop, 0, NULL); + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_loop_set_recv_delay(g[tid].loop, 0, NULL); + } /* Restore msg logger. */ msg_logger_set_enabled(enabled); @@ -1158,20 +1310,24 @@ static int tsx_uac_retransmit_test(void) ** ***************************************************************************** */ -static int tsx_resolve_error_test(void) +static int tsx_resolve_error_test(unsigned tid) { + char target[80]; int status = 0; PJ_LOG(3,(THIS_FILE, " test2: resolve error test")); + pj_ansi_snprintf(target, sizeof(target), + "sip:%d@unresolved-host;transport=%s", + tid, g[tid].test_param->tp_type); + /* * Variant (a): immediate resolve error. */ PJ_LOG(3,(THIS_FILE, " variant a: immediate resolving error")); - status = perform_tsx_test(-800, - "sip:bob@unresolved-host", - FROM_URI, TEST2_BRANCH_ID, 20, + status = perform_tsx_test(tid, -800, target, + g[tid].FROM_URI, TEST2_BRANCH_ID, 20, &pjsip_options_method); if (status != 0) return status; @@ -1182,20 +1338,24 @@ static int tsx_resolve_error_test(void) PJ_LOG(3,(THIS_FILE, " variant b: error via callback")); /* This only applies to "loop-dgram" transport */ - if (test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + int prev_fail = 0; + unsigned prev_delay = 0; + /* Set loop transport to return delayed error. */ - pjsip_loop_set_failure(loop, 2, NULL); - pjsip_loop_set_send_callback_delay(loop, 10, NULL); + pjsip_loop_set_failure(g[tid].loop, 2, &prev_fail); + pjsip_loop_set_send_callback_delay(g[tid].loop, 10, &prev_delay); - status = perform_tsx_test(-800, TARGET_URI, FROM_URI, - TEST2_BRANCH_ID, 2, + status = perform_tsx_test(tid, -800, g[tid].TARGET_URI, + g[tid].FROM_URI, TEST2_BRANCH_ID, 2, &pjsip_options_method); - if (status != 0) - return status; /* Restore loop transport settings. */ - pjsip_loop_set_failure(loop, 0, NULL); - pjsip_loop_set_send_callback_delay(loop, 0, NULL); + pjsip_loop_set_failure(g[tid].loop, prev_fail, NULL); + pjsip_loop_set_send_callback_delay(g[tid].loop, prev_delay, NULL); + + if (status != 0) + return status; } return status; @@ -1210,7 +1370,7 @@ static int tsx_resolve_error_test(void) ** ***************************************************************************** */ -static int tsx_terminate_resolving_test(void) +static int tsx_terminate_resolving_test(unsigned tid) { unsigned prev_delay; pj_status_t status; @@ -1218,14 +1378,18 @@ static int tsx_terminate_resolving_test(void) PJ_LOG(3,(THIS_FILE, " test3: terminate while resolving test")); /* Configure transport delay. */ - pjsip_loop_set_send_callback_delay(loop, 100, &prev_delay); + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_loop_set_send_callback_delay(g[tid].loop, 100, &prev_delay); + } /* Start the test. */ - status = perform_tsx_test(-900, TARGET_URI, FROM_URI, + status = perform_tsx_test(tid, -900, g[tid].TARGET_URI, g[tid].FROM_URI, TEST3_BRANCH_ID, 2, &pjsip_options_method); /* Restore delay. */ - pjsip_loop_set_send_callback_delay(loop, prev_delay, NULL); + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_loop_set_send_callback_delay(g[tid].loop, prev_delay, NULL); + } return status; } @@ -1241,7 +1405,7 @@ static int tsx_terminate_resolving_test(void) ** ***************************************************************************** */ -static int tsx_retransmit_fail_test(void) +static int tsx_retransmit_fail_test(unsigned tid) { int i; unsigned delay[] = {0, 10}; @@ -1257,13 +1421,13 @@ static int tsx_retransmit_fail_test(void) " variant %c: transport delay %d ms", ('a'+i), delay[i])); /* Configure transport delay. */ - pjsip_loop_set_send_callback_delay(loop, delay[i], NULL); + pjsip_loop_set_send_callback_delay(g[tid].loop, delay[i], NULL); /* Restore transport failure mode. */ - pjsip_loop_set_failure(loop, 0, 0); + pjsip_loop_set_failure(g[tid].loop, 0, 0); /* Start the test. */ - status = perform_tsx_test(-1000, TARGET_URI, FROM_URI, + status = perform_tsx_test(tid, -1000, g[tid].TARGET_URI, g[tid].FROM_URI, TEST4_BRANCH_ID, 6, &pjsip_options_method); if (status != 0) @@ -1272,10 +1436,10 @@ static int tsx_retransmit_fail_test(void) } /* Restore delay. */ - pjsip_loop_set_send_callback_delay(loop, 0, NULL); + pjsip_loop_set_send_callback_delay(g[tid].loop, 0, NULL); /* Restore transport failure mode. */ - pjsip_loop_set_failure(loop, 0, 0); + pjsip_loop_set_failure(g[tid].loop, 0, 0); return status; } @@ -1287,14 +1451,14 @@ static int tsx_retransmit_fail_test(void) ** ***************************************************************************** */ -static int tsx_terminate_after_retransmit_test(void) +static int tsx_terminate_after_retransmit_test(unsigned tid) { int status; PJ_LOG(3,(THIS_FILE, " test5: terminate after retransmissions")); /* Do the test. */ - status = perform_tsx_test(-1100, TARGET_URI, FROM_URI, + status = perform_tsx_test(tid, -1100, g[tid].TARGET_URI, g[tid].FROM_URI, TEST5_BRANCH_ID, 6, &pjsip_options_method); @@ -1312,7 +1476,8 @@ static int tsx_terminate_after_retransmit_test(void) ** ***************************************************************************** */ -static int perform_generic_test( const char *title, +static int perform_generic_test( unsigned tid, + const char *title, char *branch_id, const pjsip_method *method) { @@ -1320,27 +1485,30 @@ static int perform_generic_test( const char *title, unsigned delay[] = { 1, 200 }; PJ_LOG(3,(THIS_FILE, " %s", title)); - + /* Do the test. */ for (i=0; i<(int)PJ_ARRAY_SIZE(delay); ++i) { - if (test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { PJ_LOG(3,(THIS_FILE, " variant %c: with %d ms transport delay", ('a'+i), delay[i])); - pjsip_loop_set_delay(loop, delay[i]); + pjsip_loop_set_failure(g[tid].loop, 0, 0); + pjsip_loop_set_delay(g[tid].loop, delay[i]); } - status = perform_tsx_test(-1200, TARGET_URI, FROM_URI, + status = perform_tsx_test(tid, -1200, g[tid].TARGET_URI, g[tid].FROM_URI, branch_id, 10, method); if (status != 0) return status; - if (test_param->type != PJSIP_TRANSPORT_LOOP_DGRAM) + if (g[tid].test_param->type != PJSIP_TRANSPORT_LOOP_DGRAM) break; } - pjsip_loop_set_delay(loop, 0); + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_loop_set_delay(g[tid].loop, 0); + } /* Done. */ return status; @@ -1353,117 +1521,97 @@ static int perform_generic_test( const char *title, ** ***************************************************************************** */ -int tsx_uac_test(struct tsx_test_param *param) +int tsx_uac_test(unsigned tid) { - pj_sockaddr_in addr; - pj_status_t status; +#define ERR(rc__) { status=rc__; goto on_return; } + struct tsx_test_param *param = &tsx_test[tid]; + int status; - timer.tsx_key.ptr = timer.key_buf; + g[tid].timer.tsx_key.ptr = g[tid].timer.key_buf; - test_param = param; + g[tid].test_param = param; /* Get transport flag */ - tp_flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)test_param->type); - - pj_ansi_snprintf(TARGET_URI, sizeof(TARGET_URI), "sip:bob@127.0.0.1:%d;transport=%s", - param->port, param->tp_type); - pj_ansi_snprintf(FROM_URI, sizeof(FROM_URI), "sip:alice@127.0.0.1:%d;transport=%s", + g[tid].tp_flag = pjsip_transport_get_flag_from_type( + (pjsip_transport_type_e)g[tid].test_param->type); + + pj_ansi_snprintf(g[tid].TARGET_URI, sizeof(g[tid].TARGET_URI), + "sip:%d@127.0.0.1:%d;transport=%s", + tid, param->port, param->tp_type); + pj_ansi_snprintf(g[tid].FROM_URI, sizeof(g[tid].FROM_URI), + "sip:tsx_uac_test@127.0.0.1:%d;transport=%s", param->port, param->tp_type); - /* Check if loop transport is configured. */ - status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM, - &addr, sizeof(addr), NULL, &loop); - if (status != PJ_SUCCESS) { - PJ_LOG(3,(THIS_FILE, " Error: loop transport is not configured!")); - return -10; - } - - /* Register modules. */ - status = pjsip_endpt_register_module(endpt, &tsx_user); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to register module", status); - return -30; - } - status = pjsip_endpt_register_module(endpt, &msg_receiver); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to register module", status); - return -40; - } + if ((status=init_test(tid)) != 0) + return status; - /* TEST1_BRANCH_ID: Basic retransmit and timeout test. */ - status = tsx_uac_retransmit_test(); + status = tsx_uac_retransmit_test(tid); if (status != 0) - return status; + goto on_return; /* TEST2_BRANCH_ID: Resolve error test. */ - status = tsx_resolve_error_test(); + status = tsx_resolve_error_test(tid); if (status != 0) - return status; + goto on_return; /* TEST3_BRANCH_ID: UAC terminate while resolving test. */ - status = tsx_terminate_resolving_test(); + status = tsx_terminate_resolving_test(tid); if (status != 0) - return status; + goto on_return; /* TEST4_BRANCH_ID: Transport failed after several retransmissions. * Only applies to loop transport. */ - if (test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { - status = tsx_retransmit_fail_test(); + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + status = tsx_retransmit_fail_test(tid); if (status != 0) - return status; + goto on_return; } /* TEST5_BRANCH_ID: Terminate transaction after several retransmissions * Only applicable to non-reliable transports. */ - if ((tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) { - status = tsx_terminate_after_retransmit_test(); + if ((g[tid].tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) { + status = tsx_terminate_after_retransmit_test(tid); if (status != 0) - return status; + goto on_return; } /* TEST6_BRANCH_ID: Successfull non-invite transaction */ - status = perform_generic_test("test6: successfull non-invite transaction", + status = perform_generic_test(tid, + "test6: successfull non-invite transaction", TEST6_BRANCH_ID, &pjsip_options_method); if (status != 0) - return status; + goto on_return; /* TEST7_BRANCH_ID: Successfull non-invite transaction */ - status = perform_generic_test("test7: successfull non-invite transaction " + status = perform_generic_test(tid, + "test7: successfull non-invite transaction " "with provisional response", TEST7_BRANCH_ID, &pjsip_options_method); if (status != 0) - return status; + goto on_return; /* TEST8_BRANCH_ID: Failed invite transaction */ - status = perform_generic_test("test8: failed invite transaction", + status = perform_generic_test(tid, + "test8: failed invite transaction", TEST8_BRANCH_ID, &pjsip_invite_method); + if (status != 0) - return status; + goto on_return; /* TEST9_BRANCH_ID: Failed invite transaction with provisional response */ - status = perform_generic_test("test9: failed invite transaction with " + status = perform_generic_test(tid, + "test9: failed invite transaction with " "provisional response", TEST9_BRANCH_ID, &pjsip_invite_method); if (status != 0) - return status; + goto on_return; - pjsip_transport_dec_ref(loop); +on_return: + finish_test(tid); flush_events(500); - - /* Unregister modules. */ - status = pjsip_endpt_unregister_module(endpt, &tsx_user); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to unregister module", status); - return -31; - } - status = pjsip_endpt_unregister_module(endpt, &msg_receiver); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to unregister module", status); - return -41; - } - - return 0; + return status; +#undef ERR } diff --git a/pjsip/src/test/tsx_uas_test.c b/pjsip/src/test/tsx_uas_test.c index 815cbe069e..d6f23cf1ab 100644 --- a/pjsip/src/test/tsx_uas_test.c +++ b/pjsip/src/test/tsx_uas_test.c @@ -94,20 +94,22 @@ ** **/ -#define TEST1_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test1") -#define TEST2_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test2") -#define TEST3_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test3") -#define TEST4_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test4") -#define TEST5_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test5") -#define TEST6_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test6") -#define TEST7_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test7") -#define TEST8_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test8") -#define TEST9_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test9") +#define TEST1_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test01") +#define TEST2_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test02") +#define TEST3_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test03") +#define TEST4_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test04") +#define TEST5_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test05") +#define TEST6_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test06") +#define TEST7_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test07") +#define TEST8_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test08") +#define TEST9_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test09") #define TEST10_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test10") #define TEST11_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test11") #define TEST12_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test12") //#define TEST13_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test13") +#define BRANCH_LEN (7+11) + #define TEST1_STATUS_CODE 200 #define TEST2_STATUS_CODE 301 #define TEST3_PROVISIONAL_CODE PJSIP_SC_QUEUED @@ -124,19 +126,41 @@ #define TEST6_RESPONSE_COUNT 3 #define TEST7_STATUS_CODE 301 #define TEST8_STATUS_CODE 302 -#define TEST9_STATUS_CODE 301 +#define TEST9_STATUS_CODE 301 /* Must be non-2xx */ #define TEST4_TITLE "test4: absorbing request retransmission" #define TEST5_TITLE "test5: retransmit last response in PROCEEDING state" #define TEST6_TITLE "test6: retransmit last response in COMPLETED state" +/* Since several tsx_uas_test() may run concurrently, keep the global vars + * in array indexed according to the test index (tid) + */ +static struct tsx_uas_test_global_t +{ + char TARGET_URI[128]; + char FROM_URI[128]; + struct tsx_test_param *test_param; + unsigned tp_flag; + + /* Static vars, which will be reset on each test. */ + int recv_count; + pj_time_val recv_last; + pj_bool_t test_complete; + + /* Loop transport instance. */ + pjsip_transport *loop; + + /* UAS transaction key. */ + char key_buf[64]; + pj_str_t tsx_key; + + /* General timer entry to be used by tests. */ + //pj_timer_entry timer; -static char TARGET_URI[128]; -static char FROM_URI[128]; -static struct tsx_test_param *test_param; -static unsigned tp_flag; + pj_bool_t modules_registered; +} g[MAX_TSX_TESTS]; #define TEST_TIMEOUT_ERROR -30 @@ -152,7 +176,7 @@ static pjsip_module tsx_user = NULL, NULL, /* prev and next */ { "Tsx-UAS-User", 12}, /* Name. */ -1, /* Id */ - PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */ + PJSIP_MOD_PRIORITY_UA_PROXY_LAYER-1,/* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ @@ -170,7 +194,7 @@ static pjsip_module msg_sender = NULL, NULL, /* prev and next */ { "Msg-Sender", 10}, /* Name. */ -1, /* Id */ - PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */ + PJSIP_MOD_PRIORITY_UA_PROXY_LAYER-1,/* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ @@ -182,22 +206,6 @@ static pjsip_module msg_sender = NULL, /* on_tsx_state() */ }; -/* Static vars, which will be reset on each test. */ -static int recv_count; -static pj_time_val recv_last; -static pj_bool_t test_complete; - -/* Loop transport instance. */ -static pjsip_transport *loop; - -/* UAS transaction key. */ -static char key_buf[64]; -static pj_str_t tsx_key = { key_buf, 0 }; - - -/* General timer entry to be used by tests. */ -//static pj_timer_entry timer; - /* Timer to send response via transaction. */ struct response { @@ -205,6 +213,104 @@ struct response pjsip_tx_data *tdata; }; +/* Get test ID from transaction instance */ +static unsigned get_tsx_tid(const pjsip_transaction *tsx) +{ + pj_assert(tsx_user.id >= 0); + return (unsigned)(long)tsx->mod_data[tsx_user.id]; +} + + +static void init_tsx(pjsip_transaction *tsx, unsigned tid) +{ + pj_assert(tsx_user.id >= 0); + tsx->mod_data[tsx_user.id] = (void*)(long)tid; + + /* Must select specific transport to use for loop */ + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_tpselector tp_sel; + + pj_assert(g[tid].loop); + pj_bzero(&tp_sel, sizeof(tp_sel)); + tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; + tp_sel.u.transport = g[tid].loop; + pjsip_tsx_set_transport(tsx, &tp_sel); + } +} + +static int modules_reg_cnt; + +/* Register modules, taking care of multiple re-registration attempts */ +static pj_status_t register_modules(unsigned tid) +{ + int old_reg_cnt; + pj_status_t status; + + pj_enter_critical_section(); + old_reg_cnt = modules_reg_cnt++; + pj_leave_critical_section(); + + if (old_reg_cnt==0) { + PJ_TEST_SUCCESS(status=pjsip_endpt_register_module(endpt, &tsx_user), + NULL, goto on_error); + PJ_TEST_SUCCESS(status=pjsip_endpt_register_module(endpt, &msg_sender), + NULL, { + pjsip_endpt_unregister_module(endpt, &tsx_user); + goto on_error; + }); + } else { + unsigned i; + /* Make sure modules are registered, wait if necessary */ + for (i=0; i<20 && (tsx_user.id<0 || msg_sender.id<0); ++i) + pj_thread_sleep(50); + + if (tsx_user.id<0 || msg_sender.id<0) { + PJ_TEST_SUCCESS(status = PJSIP_ENOTINITIALIZED, + "other thread failed to register module", + goto on_error); + } + } + + g[tid].modules_registered = 1; + return PJ_SUCCESS; + +on_error: + pj_enter_critical_section(); + modules_reg_cnt--; + pj_leave_critical_section(); + + return status; +} + +/* Unregister modules, taking care of premature unregistration attempt */ +static void unregister_modules(unsigned tid) +{ + int new_reg_cnt; + + if (!g[tid].modules_registered) + return; + + g[tid].modules_registered = 0; + + // Note: + // on_tsx_state() can be called much later during pjsip shutdown + // i.e. when transaction layer is being destroyed. If we unregister + // the modules, get_tsx_tid() will fail with assertion. + // So just let the module registered. + return; + + pj_enter_critical_section(); + new_reg_cnt = --modules_reg_cnt; + pj_leave_critical_section(); + + if (new_reg_cnt == 0) { + PJ_TEST_SUCCESS(pjsip_endpt_unregister_module(endpt, &tsx_user), + "error ignored", {}); + PJ_TEST_SUCCESS(pjsip_endpt_unregister_module(endpt, &msg_sender), + "error ignored", {}); + } +} + /* Timer callback to send response. */ static void send_response_timer( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) @@ -241,12 +347,13 @@ static void send_response( pjsip_rx_data *rdata, { pj_status_t status; pjsip_tx_data *tdata; + unsigned tid = get_tsx_tid(tsx); status = pjsip_endpt_create_response( endpt, rdata, status_code, NULL, &tdata); if (status != PJ_SUCCESS) { app_perror(" error: unable to create response", status); - test_complete = -196; + g[tid].test_complete = -196; return; } @@ -255,13 +362,14 @@ static void send_response( pjsip_rx_data *rdata, pjsip_tx_data_dec_ref(tdata); // Some tests do expect failure! //app_perror(" error: unable to send response", status); - //test_complete = -197; + //g[tid].test_complete = -197; return; } } /* Schedule timer to send response for the specified UAS transaction */ -static void schedule_send_response( pjsip_rx_data *rdata, +static void schedule_send_response( unsigned tid, + pjsip_rx_data *rdata, const pj_str_t *tsx_key_, int status_code, int msec_delay ) @@ -276,7 +384,7 @@ static void schedule_send_response( pjsip_rx_data *rdata, &tdata); if (status != PJ_SUCCESS) { app_perror(" error: unable to create response", status); - test_complete = -198; + g[tid].test_complete = -198; return; } @@ -296,18 +404,18 @@ static void schedule_send_response( pjsip_rx_data *rdata, if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); app_perror(" error: unable to schedule timer", status); - test_complete = -199; + g[tid].test_complete = -199; return; } } /* Find and terminate tsx with the specified key. */ -static void terminate_our_tsx(int status_code) +static void terminate_our_tsx(unsigned tid, int status_code) { pjsip_transaction *tsx; - tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); + tsx = pjsip_tsx_layer_find_tsx(&g[tid].tsx_key, PJ_TRUE); if (!tsx) { PJ_LOG(3,(THIS_FILE," error: timer unable to find transaction")); return; @@ -337,7 +445,7 @@ static void schedule_terminate_tsx( pjsip_transaction *tsx, delay.msec = msec_delay; pj_time_val_normalize(&delay); - pj_assert(pj_strcmp(&tsx->transaction_key, &tsx_key)==0); + pj_assert(pj_strcmp(&tsx->transaction_key, &g[tid].tsx_key)==0); timer.user_data = NULL; timer.id = status_code; timer.cb = &terminate_tsx_timer; @@ -353,8 +461,28 @@ static void schedule_terminate_tsx( pjsip_transaction *tsx, */ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { - if (pj_stricmp2(&tsx->branch, TEST1_BRANCH_ID)==0 || - pj_stricmp2(&tsx->branch, TEST2_BRANCH_ID)==0) + unsigned tid = get_tsx_tid(tsx); + + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM && + e->type==PJSIP_EVENT_TSX_STATE) + { + if (e->body.tsx_state.type==PJSIP_EVENT_RX_MSG) { + PJ_TEST_EQ( e->body.tsx_state.src.rdata->tp_info.transport, + g[tid].loop, NULL, { + g[tid].test_complete = -704; + return; + }); + } else if (e->body.tsx_state.type==PJSIP_EVENT_TX_MSG) { + PJ_TEST_EQ( e->body.tsx_state.src.tdata->tp_info.transport, + g[tid].loop, NULL, { + g[tid].test_complete = -706; + return; + }); + } + } + + if (pj_strnicmp2(&tsx->branch, TEST1_BRANCH_ID, BRANCH_LEN)==0 || + pj_strnicmp2(&tsx->branch, TEST2_BRANCH_ID, BRANCH_LEN)==0) { /* * TEST1_BRANCH_ID tests that non-INVITE transaction transmits final @@ -363,23 +491,23 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) * * TEST2_BRANCH_ID does similar test for non-2xx final response. */ - int status_code = (pj_stricmp2(&tsx->branch, TEST1_BRANCH_ID)==0) ? + int status_code = (pj_strnicmp2(&tsx->branch, TEST1_BRANCH_ID, BRANCH_LEN)==0) ? TEST1_STATUS_CODE : TEST2_STATUS_CODE; if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - test_complete = 1; + g[tid].test_complete = 1; /* Check that status code is status_code. */ if (tsx->status_code != status_code) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -100; + g[tid].test_complete = -100; } /* Previous state must be completed. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -101; + g[tid].test_complete = -101; } } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) { @@ -387,30 +515,30 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Previous state must be TRYING. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -102; + g[tid].test_complete = -102; } } } else - if (pj_stricmp2(&tsx->branch, TEST3_BRANCH_ID)==0) { + if (pj_strnicmp2(&tsx->branch, TEST3_BRANCH_ID, BRANCH_LEN)==0) { /* * TEST3_BRANCH_ID tests sending provisional response. */ if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - test_complete = 1; + g[tid].test_complete = 1; /* Check that status code is status_code. */ if (tsx->status_code != TEST3_STATUS_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -110; + g[tid].test_complete = -110; } /* Previous state must be completed. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -111; + g[tid].test_complete = -111; } } else if (tsx->state == PJSIP_TSX_STATE_PROCEEDING) { @@ -418,19 +546,19 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Previous state must be TRYING. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -112; + g[tid].test_complete = -112; } /* Check that status code is status_code. */ if (tsx->status_code != TEST3_PROVISIONAL_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -113; + g[tid].test_complete = -113; } /* Check that event must be TX_MSG */ if (e->body.tsx_state.type != PJSIP_EVENT_TX_MSG) { PJ_LOG(3,(THIS_FILE, " error: incorrect event")); - test_complete = -114; + g[tid].test_complete = -114; } } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) { @@ -438,25 +566,25 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Previous state must be PROCEEDING. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_PROCEEDING) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -115; + g[tid].test_complete = -115; } /* Check that status code is status_code. */ if (tsx->status_code != TEST3_STATUS_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -116; + g[tid].test_complete = -116; } /* Check that event must be TX_MSG */ if (e->body.tsx_state.type != PJSIP_EVENT_TX_MSG) { PJ_LOG(3,(THIS_FILE, " error: incorrect event")); - test_complete = -117; + g[tid].test_complete = -117; } } } else - if (pj_stricmp2(&tsx->branch, TEST4_BRANCH_ID)==0) { + if (pj_strnicmp2(&tsx->branch, TEST4_BRANCH_ID, BRANCH_LEN)==0) { /* * TEST4_BRANCH_ID tests receiving retransmissions in TRYING state. */ @@ -470,26 +598,26 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) " error: incorrect status code %d " "(expecting %d)", tsx->status_code, TEST4_STATUS_CODE)); - test_complete = -120; + g[tid].test_complete = -120; } /* Previous state. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -121; + g[tid].test_complete = -121; } } else if (tsx->state != PJSIP_TSX_STATE_DESTROYED) { PJ_LOG(3,(THIS_FILE, " error: unexpected state %s (122)", pjsip_tsx_state_str(tsx->state))); - test_complete = -122; + g[tid].test_complete = -122; } } else - if (pj_stricmp2(&tsx->branch, TEST5_BRANCH_ID)==0) { + if (pj_strnicmp2(&tsx->branch, TEST5_BRANCH_ID, BRANCH_LEN)==0) { /* * TEST5_BRANCH_ID tests receiving retransmissions in PROCEEDING state */ @@ -501,13 +629,13 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Check that status code is status_code. */ if (tsx->status_code != TEST5_STATUS_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -130; + g[tid].test_complete = -130; } /* Previous state. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_PROCEEDING) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -131; + g[tid].test_complete = -131; } } else if (tsx->state == PJSIP_TSX_STATE_PROCEEDING) { @@ -515,18 +643,18 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Check status code. */ if (tsx->status_code != TEST5_PROVISIONAL_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -132; + g[tid].test_complete = -132; } } else if (tsx->state != PJSIP_TSX_STATE_DESTROYED) { PJ_LOG(3,(THIS_FILE, " error: unexpected state %s (133)", pjsip_tsx_state_str(tsx->state))); - test_complete = -133; + g[tid].test_complete = -133; } } else - if (pj_stricmp2(&tsx->branch, TEST6_BRANCH_ID)==0) { + if (pj_strnicmp2(&tsx->branch, TEST6_BRANCH_ID, BRANCH_LEN)==0) { /* * TEST6_BRANCH_ID tests receiving retransmissions in COMPLETED state */ @@ -540,13 +668,13 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: incorrect status code %d " "(expecting %d)", tsx->status_code, TEST6_STATUS_CODE)); - test_complete = -140; + g[tid].test_complete = -140; } /* Previous state. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -141; + g[tid].test_complete = -141; } } else if (tsx->state != PJSIP_TSX_STATE_PROCEEDING && @@ -555,14 +683,14 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { PJ_LOG(3,(THIS_FILE, " error: unexpected state %s (142)", pjsip_tsx_state_str(tsx->state))); - test_complete = -142; + g[tid].test_complete = -142; } } else - if (pj_stricmp2(&tsx->branch, TEST7_BRANCH_ID)==0 || - pj_stricmp2(&tsx->branch, TEST8_BRANCH_ID)==0) + if (pj_strnicmp2(&tsx->branch, TEST7_BRANCH_ID, BRANCH_LEN)==0 || + pj_strnicmp2(&tsx->branch, TEST8_BRANCH_ID, BRANCH_LEN)==0) { /* * TEST7_BRANCH_ID and TEST8_BRANCH_ID test retransmission of @@ -570,7 +698,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) */ int code; - if (pj_stricmp2(&tsx->branch, TEST7_BRANCH_ID) == 0) + if (pj_strnicmp2(&tsx->branch, TEST7_BRANCH_ID, BRANCH_LEN) == 0) code = TEST7_STATUS_CODE; else code = TEST8_STATUS_CODE; @@ -580,27 +708,28 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - if (test_complete == 0) - test_complete = 1; + if (g[tid].test_complete == 0) + g[tid].test_complete = 1; /* Check status code. */ if (tsx->status_code != PJSIP_SC_TSX_TIMEOUT) { - PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -150; + PJ_LOG(3,(THIS_FILE, " error: incorrect status code %d", + tsx->status_code)); + g[tid].test_complete = -150; } /* Previous state. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -151; + g[tid].test_complete = -151; } /* Check the number of retransmissions */ - if (tp_flag & PJSIP_TRANSPORT_RELIABLE) { + if (g[tid].tp_flag & PJSIP_TRANSPORT_RELIABLE) { if (tsx->retransmit_count != 0) { PJ_LOG(3,(THIS_FILE, " error: should not retransmit")); - test_complete = -1510; + g[tid].test_complete = -1510; } } else { @@ -610,7 +739,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) " error: incorrect retransmit count %d " "(expecting 10)", tsx->retransmit_count)); - test_complete = -1510; + g[tid].test_complete = -1510; } } @@ -620,25 +749,25 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Check that status code is status_code. */ if (tsx->status_code != code) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -152; + g[tid].test_complete = -152; } /* Previous state. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -153; + g[tid].test_complete = -153; } } else if (tsx->state != PJSIP_TSX_STATE_DESTROYED) { PJ_LOG(3,(THIS_FILE, " error: unexpected state (154)")); - test_complete = -154; + g[tid].test_complete = -154; } } else - if (pj_stricmp2(&tsx->branch, TEST9_BRANCH_ID)==0) { + if (pj_strnicmp2(&tsx->branch, TEST9_BRANCH_ID, BRANCH_LEN)==0) { /* * TEST9_BRANCH_ID tests that retransmission of INVITE final response * must cease when ACK is received. @@ -649,33 +778,29 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - if (test_complete == 0) - test_complete = 1; + if (g[tid].test_complete == 0) + g[tid].test_complete = 1; /* Check status code. */ - if (tsx->status_code != TEST9_STATUS_CODE) { - PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -160; - } + PJ_TEST_EQ(tsx->status_code, TEST9_STATUS_CODE, NULL, + g[tid].test_complete = -160); /* Previous state. */ - if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_CONFIRMED) { - PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -161; - } + PJ_TEST_EQ(e->body.tsx_state.prev_state, PJSIP_TSX_STATE_CONFIRMED, + NULL, g[tid].test_complete = -161); } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) { /* Check that status code is status_code. */ if (tsx->status_code != TEST9_STATUS_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -162; + g[tid].test_complete = -162; } /* Previous state. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -163; + g[tid].test_complete = -163; } @@ -684,31 +809,31 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Check that status code is status_code. */ if (tsx->status_code != TEST9_STATUS_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -164; + g[tid].test_complete = -164; } /* Previous state. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -165; + g[tid].test_complete = -165; } } else if (tsx->state != PJSIP_TSX_STATE_DESTROYED) { PJ_LOG(3,(THIS_FILE, " error: unexpected state (166)")); - test_complete = -166; + g[tid].test_complete = -166; } } else - if (pj_stricmp2(&tsx->branch, TEST10_BRANCH_ID)==0 || - pj_stricmp2(&tsx->branch, TEST12_BRANCH_ID)==0) + if (pj_strnicmp2(&tsx->branch, TEST10_BRANCH_ID, BRANCH_LEN)==0 || + pj_strnicmp2(&tsx->branch, TEST12_BRANCH_ID, BRANCH_LEN)==0) { if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - if (!test_complete) - test_complete = 1; + if (!g[tid].test_complete) + g[tid].test_complete = 1; if (tsx->status_code != PJSIP_SC_REQUEST_TIMEOUT) { PJ_LOG(3,(THIS_FILE," error: incorrect status code" @@ -716,16 +841,16 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJSIP_SC_REQUEST_TIMEOUT, // PJSIP_SC_TSX_TRANSPORT_ERROR, tsx->status_code)); - test_complete = -170; + g[tid].test_complete = -170; } } } else - if (pj_stricmp2(&tsx->branch, TEST11_BRANCH_ID)==0) + if (pj_strnicmp2(&tsx->branch, TEST11_BRANCH_ID, BRANCH_LEN)==0) { if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - if (!test_complete) - test_complete = 1; + if (!g[tid].test_complete) + g[tid].test_complete = 1; if (tsx->status_code != PJSIP_SC_REQUEST_TIMEOUT && tsx->status_code != PJSIP_SC_OK) @@ -735,7 +860,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJSIP_SC_REQUEST_TIMEOUT, // PJSIP_SC_TSX_TRANSPORT_ERROR, tsx->status_code)); - test_complete = -170; + g[tid].test_complete = -170; } } } @@ -745,9 +870,12 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) static void save_key(pjsip_transaction *tsx) { pj_str_t key; + unsigned tid = get_tsx_tid(tsx); + g[tid].tsx_key.ptr = g[tid].key_buf; + g[tid].tsx_key.slen = 0; pj_strdup(tsx->pool, &key, &tsx->transaction_key); - pj_strcpy(&tsx_key, &key); + pj_strcpy(&g[tid].tsx_key, &key); } #define DIFF(a,b) ((amsg_info.msg; pj_str_t branch_param = rdata->msg_info.via->branch_param; pj_status_t status; + pjsip_to_hdr *to_hdr = rdata->msg_info.to; + pjsip_sip_uri *target = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri); + pjsip_from_hdr *from_hdr = rdata->msg_info.from; + pjsip_sip_uri *from_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(from_hdr->uri); + unsigned tid; + + if (pj_strcmp2(&from_uri->user, "tsx_uas_test")) { + /* Not our message */ + return PJ_FALSE; + } - if (pj_stricmp2(&branch_param, TEST1_BRANCH_ID) == 0 || - pj_stricmp2(&branch_param, TEST2_BRANCH_ID) == 0) + tid = (unsigned)pj_strtol(&target->user); + + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + PJ_TEST_EQ(rdata->tp_info.transport, g[tid].loop, NULL, + {g[tid].test_complete = -602; return PJ_TRUE;}); + } + + if (pj_strnicmp2(&branch_param, TEST1_BRANCH_ID, BRANCH_LEN) == 0 || + pj_strnicmp2(&branch_param, TEST2_BRANCH_ID, BRANCH_LEN) == 0) { /* * TEST1_BRANCH_ID tests that non-INVITE transaction transmits 2xx @@ -771,7 +916,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) * * TEST2_BRANCH_ID performs similar test for non-2xx final response. */ - int status_code = (pj_stricmp2(&branch_param, TEST1_BRANCH_ID) == 0) ? + int status_code = (pj_strnicmp2(&branch_param, TEST1_BRANCH_ID, BRANCH_LEN) == 0) ? TEST1_STATUS_CODE : TEST2_STATUS_CODE; if (msg->type == PJSIP_REQUEST_MSG) { @@ -783,9 +928,10 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx); if (status != PJ_SUCCESS) { app_perror(" error: unable to create transaction", status); - test_complete = -110; + g[tid].test_complete = -110; return PJ_TRUE; } + init_tsx(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); @@ -794,24 +940,25 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } else { /* Verify the response received. */ - ++recv_count; + ++g[tid].recv_count; /* Verify status code. */ if (msg->line.status.code != status_code) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -113; + g[tid].test_complete = -113; } /* Verify that no retransmissions is received. */ - if (recv_count > 1) { + if (g[tid].recv_count > 1) { PJ_LOG(3,(THIS_FILE, " error: retransmission received")); - test_complete = -114; + g[tid].test_complete = -114; } } return PJ_TRUE; - } else if (pj_stricmp2(&branch_param, TEST3_BRANCH_ID) == 0) { + } else if (pj_strnicmp2(&branch_param, TEST3_BRANCH_ID, + BRANCH_LEN) == 0) { /* TEST3_BRANCH_ID tests provisional response. */ @@ -824,45 +971,46 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx); if (status != PJ_SUCCESS) { app_perror(" error: unable to create transaction", status); - test_complete = -116; + g[tid].test_complete = -116; return PJ_TRUE; } + init_tsx(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); send_response(rdata, tsx, TEST3_PROVISIONAL_CODE); - schedule_send_response(rdata, &tsx->transaction_key, + schedule_send_response(tid, rdata, &tsx->transaction_key, TEST3_STATUS_CODE, 2000); } else { /* Verify the response received. */ - ++recv_count; + ++g[tid].recv_count; - if (recv_count == 1) { + if (g[tid].recv_count == 1) { /* Verify status code. */ if (msg->line.status.code != TEST3_PROVISIONAL_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -123; + g[tid].test_complete = -123; } - } else if (recv_count == 2) { + } else if (g[tid].recv_count == 2) { /* Verify status code. */ if (msg->line.status.code != TEST3_STATUS_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -124; + g[tid].test_complete = -124; } } else { PJ_LOG(3,(THIS_FILE, " error: retransmission received")); - test_complete = -125; + g[tid].test_complete = -125; } } return PJ_TRUE; - } else if (pj_stricmp2(&branch_param, TEST4_BRANCH_ID) == 0 || - pj_stricmp2(&branch_param, TEST5_BRANCH_ID) == 0 || - pj_stricmp2(&branch_param, TEST6_BRANCH_ID) == 0) + } else if (pj_strnicmp2(&branch_param, TEST4_BRANCH_ID, BRANCH_LEN) == 0 || + pj_strnicmp2(&branch_param, TEST5_BRANCH_ID, BRANCH_LEN) == 0 || + pj_strnicmp2(&branch_param, TEST6_BRANCH_ID, BRANCH_LEN) == 0) { /* TEST4_BRANCH_ID: absorbs retransmissions in TRYING state. */ @@ -878,19 +1026,19 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx); if (status != PJ_SUCCESS) { app_perror(" error: unable to create transaction", status); - test_complete = -130; + g[tid].test_complete = -130; return PJ_TRUE; } - + init_tsx(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); - if (pj_stricmp2(&branch_param, TEST4_BRANCH_ID) == 0) { + if (pj_strnicmp2(&branch_param, TEST4_BRANCH_ID, BRANCH_LEN) == 0) { - } else if (pj_stricmp2(&branch_param, TEST5_BRANCH_ID) == 0) { + } else if (pj_strnicmp2(&branch_param, TEST5_BRANCH_ID, BRANCH_LEN) == 0) { send_response(rdata, tsx, TEST5_PROVISIONAL_CODE); - } else if (pj_stricmp2(&branch_param, TEST6_BRANCH_ID) == 0) { + } else if (pj_strnicmp2(&branch_param, TEST6_BRANCH_ID, BRANCH_LEN) == 0) { PJ_LOG(4,(THIS_FILE, " sending provisional response")); send_response(rdata, tsx, TEST6_PROVISIONAL_CODE); PJ_LOG(4,(THIS_FILE, " sending final response")); @@ -900,35 +1048,35 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } else { /* Verify the response received. */ - PJ_LOG(4,(THIS_FILE, " received response number %d", recv_count)); + PJ_LOG(4,(THIS_FILE, " received response number %d", g[tid].recv_count)); - ++recv_count; + ++g[tid].recv_count; - if (pj_stricmp2(&branch_param, TEST4_BRANCH_ID) == 0) { + if (pj_strnicmp2(&branch_param, TEST4_BRANCH_ID, BRANCH_LEN) == 0) { PJ_LOG(3,(THIS_FILE, " error: not expecting response!")); - test_complete = -132; + g[tid].test_complete = -132; - } else if (pj_stricmp2(&branch_param, TEST5_BRANCH_ID) == 0) { + } else if (pj_strnicmp2(&branch_param, TEST5_BRANCH_ID, BRANCH_LEN) == 0) { if (rdata->msg_info.msg->line.status.code!=TEST5_PROVISIONAL_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code!")); - test_complete = -133; + g[tid].test_complete = -133; } - if (recv_count > TEST5_RESPONSE_COUNT) { + if (g[tid].recv_count > TEST5_RESPONSE_COUNT) { PJ_LOG(3,(THIS_FILE, " error: not expecting response!")); - test_complete = -134; + g[tid].test_complete = -134; } - } else if (pj_stricmp2(&branch_param, TEST6_BRANCH_ID) == 0) { + } else if (pj_strnicmp2(&branch_param, TEST6_BRANCH_ID, BRANCH_LEN) == 0) { int code = rdata->msg_info.msg->line.status.code; - switch (recv_count) { + switch (g[tid].recv_count) { case 1: if (code != TEST6_PROVISIONAL_CODE) { PJ_LOG(3,(THIS_FILE, " error: invalid code!")); - test_complete = -135; + g[tid].test_complete = -135; } break; case 2: @@ -936,12 +1084,12 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) if (code != TEST6_STATUS_CODE) { PJ_LOG(3,(THIS_FILE, " error: invalid code %d " "(expecting %d)", code, TEST6_STATUS_CODE)); - test_complete = -136; + g[tid].test_complete = -136; } break; default: PJ_LOG(3,(THIS_FILE, " error: not expecting response")); - test_complete = -137; + g[tid].test_complete = -137; break; } } @@ -949,10 +1097,9 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) return PJ_TRUE; - } else if (pj_stricmp2(&branch_param, TEST7_BRANCH_ID) == 0 || - pj_stricmp2(&branch_param, TEST8_BRANCH_ID) == 0) + } else if (pj_strnicmp2(&branch_param, TEST7_BRANCH_ID, BRANCH_LEN) == 0 || + pj_strnicmp2(&branch_param, TEST8_BRANCH_ID, BRANCH_LEN) == 0) { - /* * TEST7_BRANCH_ID and TEST8_BRANCH_ID test the retransmission * of INVITE final response @@ -965,14 +1112,14 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx); if (status != PJ_SUCCESS) { app_perror(" error: unable to create transaction", status); - test_complete = -140; + g[tid].test_complete = -140; return PJ_TRUE; } - + init_tsx(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); - if (pj_stricmp2(&branch_param, TEST7_BRANCH_ID) == 0) { + if (pj_strnicmp2(&branch_param, TEST7_BRANCH_ID, BRANCH_LEN) == 0) { send_response(rdata, tsx, TEST7_STATUS_CODE); @@ -985,21 +1132,22 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } else { int code; - ++recv_count; + ++g[tid].recv_count; - if (pj_stricmp2(&branch_param, TEST7_BRANCH_ID) == 0) + if (pj_strnicmp2(&branch_param, TEST7_BRANCH_ID, BRANCH_LEN) == 0) code = TEST7_STATUS_CODE; else code = TEST8_STATUS_CODE; - if (recv_count==1) { + if (g[tid].recv_count==1) { if (rdata->msg_info.msg->line.status.code != code) { - PJ_LOG(3,(THIS_FILE," error: invalid status code")); - test_complete = -141; + PJ_LOG(3,(THIS_FILE," error: invalid status code %d", + rdata->msg_info.msg->line.status.code)); + g[tid].test_complete = -141; } - recv_last = rdata->pkt_info.timestamp; + g[tid].recv_last = rdata->pkt_info.timestamp; } else { @@ -1008,10 +1156,10 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) now = rdata->pkt_info.timestamp; - PJ_TIME_VAL_SUB(now, recv_last); + PJ_TIME_VAL_SUB(now, g[tid].recv_last); msec = now.sec*1000 + now.msec; - msec_expected = (1 << (recv_count-2)) * pjsip_cfg()->tsx.t1; + msec_expected = (1 << (g[tid].recv_count-2)) * pjsip_cfg()->tsx.t1; if (msec_expected > pjsip_cfg()->tsx.t2) msec_expected = pjsip_cfg()->tsx.t2; @@ -1020,22 +1168,22 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) " error: incorrect retransmission " "time (%d ms expected, %d ms received", msec_expected, msec)); - test_complete = -142; + g[tid].test_complete = -142; } - if (recv_count > 11) { + if (g[tid].recv_count > 11) { PJ_LOG(3,(THIS_FILE," error: too many responses (%d)", - recv_count)); - test_complete = -143; + g[tid].recv_count)); + g[tid].test_complete = -143; } - recv_last = rdata->pkt_info.timestamp; + g[tid].recv_last = rdata->pkt_info.timestamp; } } return PJ_TRUE; - } else if (pj_stricmp2(&branch_param, TEST9_BRANCH_ID) == 0) { + } else if (pj_strnicmp2(&branch_param, TEST9_BRANCH_ID, BRANCH_LEN) == 0) { /* * TEST9_BRANCH_ID tests that the retransmission of INVITE final @@ -1050,10 +1198,10 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx); if (status != PJ_SUCCESS) { app_perror(" error: unable to create transaction", status); - test_complete = -150; + g[tid].test_complete = -150; return PJ_TRUE; } - + init_tsx(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); send_response(rdata, tsx, TEST9_STATUS_CODE); @@ -1061,18 +1209,16 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } else { - ++recv_count; + ++g[tid].recv_count; - if (rdata->msg_info.msg->line.status.code != TEST9_STATUS_CODE) { - PJ_LOG(3,(THIS_FILE," error: invalid status code")); - test_complete = -151; - } + PJ_TEST_EQ(rdata->msg_info.msg->line.status.code, TEST9_STATUS_CODE, + NULL, g[tid].test_complete = -151); - if (recv_count==1) { + if (g[tid].recv_count==1) { - recv_last = rdata->pkt_info.timestamp; + g[tid].recv_last = rdata->pkt_info.timestamp; - } else if (recv_count < 5) { + } else if (g[tid].recv_count < 5) { /* Let UAS retransmit some messages before we send ACK. */ pj_time_val now; @@ -1080,10 +1226,10 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) now = rdata->pkt_info.timestamp; - PJ_TIME_VAL_SUB(now, recv_last); + PJ_TIME_VAL_SUB(now, g[tid].recv_last); msec = now.sec*1000 + now.msec; - msec_expected = (1 << (recv_count-2)) * pjsip_cfg()->tsx.t1; + msec_expected = (1 << (g[tid].recv_count-2)) * pjsip_cfg()->tsx.t1; if (msec_expected > pjsip_cfg()->tsx.t2) msec_expected = pjsip_cfg()->tsx.t2; @@ -1092,12 +1238,12 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) " error: incorrect retransmission " "time (%d ms expected, %d ms received", msec_expected, msec)); - test_complete = -152; + g[tid].test_complete = -152; } - recv_last = rdata->pkt_info.timestamp; + g[tid].recv_last = rdata->pkt_info.timestamp; - } else if (recv_count == 5) { + } else if (g[tid].recv_count == 5) { pjsip_tx_data *tdata; pjsip_sip_uri *uri; pjsip_via_hdr *via; @@ -1114,41 +1260,53 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) &tdata); if (status != PJ_SUCCESS) { app_perror(" error: unable to create ACK", status); - test_complete = -153; + g[tid].test_complete = -153; return PJ_TRUE; } uri=(pjsip_sip_uri*)pjsip_uri_get_uri(tdata->msg->line.req.uri); - uri->transport_param = pj_str("loop-dgram"); + pj_strdup2(tdata->pool, &uri->transport_param, + g[tid].test_param->tp_type); + uri->port = g[tid].test_param->port; via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); - via->branch_param = pj_str(TEST9_BRANCH_ID); + pj_strdup(tdata->pool, &via->branch_param, + &rdata->msg_info.via->branch_param); + + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_tpselector tp_sel; + + pj_bzero(&tp_sel, sizeof(tp_sel)); + tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; + tp_sel.u.transport = g[tid].loop; + pjsip_tx_data_set_transport(tdata, &tp_sel); + } status = pjsip_endpt_send_request_stateless(endpt, tdata, NULL, NULL); if (status != PJ_SUCCESS) { app_perror(" error: unable to send ACK", status); - test_complete = -154; + g[tid].test_complete = -154; } } else { PJ_LOG(3,(THIS_FILE," error: too many responses (%d)", - recv_count)); - test_complete = -155; + g[tid].recv_count)); + g[tid].test_complete = -155; } } return PJ_TRUE; - } else if (pj_stricmp2(&branch_param, TEST10_BRANCH_ID) == 0 || - pj_stricmp2(&branch_param, TEST11_BRANCH_ID) == 0 || - pj_stricmp2(&branch_param, TEST12_BRANCH_ID) == 0) + } else if (pj_strnicmp2(&branch_param, TEST10_BRANCH_ID, BRANCH_LEN) == 0 || + pj_strnicmp2(&branch_param, TEST11_BRANCH_ID, BRANCH_LEN) == 0 || + pj_strnicmp2(&branch_param, TEST12_BRANCH_ID, BRANCH_LEN) == 0) { int test_num, code1, code2; - if (pj_stricmp2(&branch_param, TEST10_BRANCH_ID) == 0) + if (pj_strnicmp2(&branch_param, TEST10_BRANCH_ID, BRANCH_LEN) == 0) test_num=10, code1 = 100, code2 = 0; - else if (pj_stricmp2(&branch_param, TEST11_BRANCH_ID) == 0) + else if (pj_strnicmp2(&branch_param, TEST11_BRANCH_ID, BRANCH_LEN) == 0) test_num=11, code1 = 100, code2 = 200; else test_num=12, code1 = 200, code2 = 0; @@ -1163,17 +1321,17 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx); if (status != PJ_SUCCESS) { app_perror(" error: unable to create transaction", status); - test_complete = -150; + g[tid].test_complete = -150; return PJ_TRUE; } - + init_tsx(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); - schedule_send_response(rdata, &tsx_key, code1, 1000); + schedule_send_response(tid, rdata, &g[tid].tsx_key, code1, 1000); if (code2) - schedule_send_response(rdata, &tsx_key, code2, 2000); + schedule_send_response(tid, rdata, &g[tid].tsx_key, code2, 2000); } else { @@ -1188,7 +1346,8 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) /* * The generic test framework, used by most of the tests. */ -static int perform_test( char *target_uri, char *from_uri, +static int perform_test( unsigned tid, + char *target_uri, char *from_uri, char *branch_param, int test_time, const pjsip_method *method, int request_cnt, int request_interval_msec, @@ -1197,10 +1356,14 @@ static int perform_test( char *target_uri, char *from_uri, pjsip_tx_data *tdata; pj_str_t target, from; pjsip_via_hdr *via; + char branch_buf[BRANCH_LEN+20]; pj_time_val timeout, next_send; int sent_cnt; + pjsip_tpselector tp_sel; pj_status_t status; + PJ_TEST_EQ(strlen(branch_param), BRANCH_LEN, NULL, return -99); + if (test_time > 0) { PJ_LOG(3,(THIS_FILE, " please standby, this will take at most %d seconds..", @@ -1208,9 +1371,9 @@ static int perform_test( char *target_uri, char *from_uri, } /* Reset test. */ - recv_count = 0; - test_complete = 0; - tsx_key.slen = 0; + g[tid].recv_count = 0; + g[tid].test_complete = 0; + g[tid].tsx_key.slen = 0; /* Init headers. */ target = pj_str(target_uri); @@ -1225,9 +1388,22 @@ static int perform_test( char *target_uri, char *from_uri, return -10; } - /* Set the branch param for test 1. */ + /* Set the branch param. Note that other tsx_uas_test() instances may + * be running simultaneously, thus the branch ID needs to be made unique + * by adding tid */ via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); - via->branch_param = pj_str(branch_param); + pj_ansi_snprintf(branch_buf, sizeof(branch_buf), + "%s-%02d", branch_param, tid); + pj_strdup2(tdata->pool, &via->branch_param, branch_buf); + + /* Must select specific transport to use */ + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pj_assert(g[tid].loop); + pj_bzero(&tp_sel, sizeof(tp_sel)); + tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; + tp_sel.u.transport = g[tid].loop; + pjsip_tx_data_set_transport(tdata, &tp_sel); + } /* Schedule first send. */ sent_cnt = 0; @@ -1239,7 +1415,7 @@ static int perform_test( char *target_uri, char *from_uri, timeout.sec += test_time; /* Wait until test complete. */ - while (!test_complete) { + while (!g[tid].test_complete) { pj_time_val now, poll_delay = {0, 10}; pjsip_endpt_handle_events(endpt, &poll_delay); @@ -1279,24 +1455,24 @@ static int perform_test( char *target_uri, char *from_uri, } } - if (test_complete < 0) { + if (g[tid].test_complete < 0) { pjsip_transaction *tsx; - tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); + tsx = pjsip_tsx_layer_find_tsx(&g[tid].tsx_key, PJ_TRUE); if (tsx) { pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED); pj_grp_lock_release(tsx->grp_lock); flush_events(1000); } pjsip_tx_data_dec_ref(tdata); - return test_complete; + return g[tid].test_complete; } /* Allow transaction to destroy itself */ flush_events(500); /* Make sure transaction has been destroyed. */ - if (pjsip_tsx_layer_find_tsx(&tsx_key, PJ_FALSE) != NULL) { + if (pjsip_tsx_layer_find_tsx(&g[tid].tsx_key, PJ_FALSE) != NULL) { PJ_LOG(3,(THIS_FILE, " Error: transaction has not been destroyed")); pjsip_tx_data_dec_ref(tdata); return -40; @@ -1325,7 +1501,7 @@ static int perform_test( char *target_uri, char *from_uri, ** ***************************************************************************** */ -static int tsx_basic_final_response_test(void) +static int tsx_basic_final_response_test(unsigned tid) { unsigned duration; int status; @@ -1335,16 +1511,16 @@ static int tsx_basic_final_response_test(void) /* Test duration must be greater than 32 secs if unreliable transport * is used. */ - duration = (tp_flag & PJSIP_TRANSPORT_RELIABLE) ? 1 : 33; + duration = (g[tid].tp_flag & PJSIP_TRANSPORT_RELIABLE) ? 1 : 33; - status = perform_test(TARGET_URI, FROM_URI, TEST1_BRANCH_ID, + status = perform_test(tid, g[tid].TARGET_URI, g[tid].FROM_URI, TEST1_BRANCH_ID, duration, &pjsip_options_method, 1, 0, 0); if (status != 0) return status; PJ_LOG(3,(THIS_FILE," test2: basic sending non-2xx final response")); - status = perform_test(TARGET_URI, FROM_URI, TEST2_BRANCH_ID, + status = perform_test(tid, g[tid].TARGET_URI, g[tid].FROM_URI, TEST2_BRANCH_ID, duration, &pjsip_options_method, 1, 0, 0); if (status != 0) return status; @@ -1359,17 +1535,17 @@ static int tsx_basic_final_response_test(void) ** ***************************************************************************** */ -static int tsx_basic_provisional_response_test(void) +static int tsx_basic_provisional_response_test(unsigned tid) { unsigned duration; int status; PJ_LOG(3,(THIS_FILE," test3: basic sending 2xx final response")); - duration = (tp_flag & PJSIP_TRANSPORT_RELIABLE) ? 1 : 33; + duration = (g[tid].tp_flag & PJSIP_TRANSPORT_RELIABLE) ? 1 : 33; duration += 2; - status = perform_test(TARGET_URI, FROM_URI, TEST3_BRANCH_ID, duration, + status = perform_test(tid, g[tid].TARGET_URI, g[tid].FROM_URI, TEST3_BRANCH_ID, duration, &pjsip_options_method, 1, 0, 0); return status; @@ -1384,7 +1560,8 @@ static int tsx_basic_provisional_response_test(void) ** ***************************************************************************** */ -static int tsx_retransmit_last_response_test(const char *title, +static int tsx_retransmit_last_response_test(unsigned tid, + const char *title, char *branch_id, int request_cnt, int status_code) @@ -1393,7 +1570,7 @@ static int tsx_retransmit_last_response_test(const char *title, PJ_LOG(3,(THIS_FILE," %s", title)); - status = perform_test(TARGET_URI, FROM_URI, branch_id, 5, + status = perform_test(tid, g[tid].TARGET_URI, g[tid].FROM_URI, branch_id, 5, &pjsip_options_method, request_cnt, 1000, 1); if (status && status != TEST_TIMEOUT_ERROR) @@ -1403,11 +1580,11 @@ static int tsx_retransmit_last_response_test(const char *title, return -31; } - terminate_our_tsx(status_code); + terminate_our_tsx(tid, status_code); flush_events(100); - if (test_complete != 1) - return test_complete; + if (g[tid].test_complete != 1) + return g[tid].test_complete; flush_events(100); return 0; @@ -1420,14 +1597,14 @@ static int tsx_retransmit_last_response_test(const char *title, ** ***************************************************************************** */ -static int tsx_final_response_retransmission_test(void) +static int tsx_final_response_retransmission_test(unsigned tid) { int status; PJ_LOG(3,(THIS_FILE, " test7: INVITE non-2xx final response retransmission")); - status = perform_test(TARGET_URI, FROM_URI, TEST7_BRANCH_ID, + status = perform_test(tid, g[tid].TARGET_URI, g[tid].FROM_URI, TEST7_BRANCH_ID, 33, /* Test duration must be greater than 32 secs */ &pjsip_invite_method, 1, 0, 0); if (status != 0) @@ -1436,7 +1613,7 @@ static int tsx_final_response_retransmission_test(void) PJ_LOG(3,(THIS_FILE, " test8: INVITE 2xx final response retransmission")); - status = perform_test(TARGET_URI, FROM_URI, TEST8_BRANCH_ID, + status = perform_test(tid, g[tid].TARGET_URI, g[tid].FROM_URI, TEST8_BRANCH_ID, 33, /* Test duration must be greater than 32 secs */ &pjsip_invite_method, 1, 0, 0); if (status != 0) @@ -1453,14 +1630,14 @@ static int tsx_final_response_retransmission_test(void) ** ***************************************************************************** */ -static int tsx_ack_test(void) +static int tsx_ack_test(unsigned tid) { int status; PJ_LOG(3,(THIS_FILE, " test9: receiving ACK for non-2xx final response")); - status = perform_test(TARGET_URI, FROM_URI, TEST9_BRANCH_ID, + status = perform_test(tid, g[tid].TARGET_URI, g[tid].FROM_URI, TEST9_BRANCH_ID, 20, /* allow 5 retransmissions */ &pjsip_invite_method, 1, 0, 0); if (status != 0) @@ -1481,7 +1658,7 @@ static int tsx_ack_test(void) ** ***************************************************************************** */ -int tsx_transport_failure_test(void) +static int tsx_transport_failure_test(unsigned tid) { struct test_desc { @@ -1515,10 +1692,10 @@ int tsx_transport_failure_test(void) pj_time_val fail_time, end_test, now; PJ_LOG(3,(THIS_FILE, " %s", tests[i].title)); - pjsip_loop_set_failure(loop, 0, NULL); - pjsip_loop_set_delay(loop, tests[i].transport_delay); + pjsip_loop_set_failure(g[tid].loop, 0, NULL); + pjsip_loop_set_delay(g[tid].loop, tests[i].transport_delay); - status = perform_test(TARGET_URI, FROM_URI, tests[i].branch_id, + status = perform_test(tid, g[tid].TARGET_URI, g[tid].FROM_URI, tests[i].branch_id, 0, &pjsip_invite_method, 1, 0, 1); if (status && status != TEST_TIMEOUT_ERROR) return status; @@ -1532,24 +1709,24 @@ int tsx_transport_failure_test(void) pj_time_val_normalize(&fail_time); do { - pj_time_val interval = { 0, 1 }; + pj_time_val interval = { 0, 10 }; pj_gettimeofday(&now); pjsip_endpt_handle_events(endpt, &interval); } while (PJ_TIME_VAL_LT(now, fail_time)); - pjsip_loop_set_failure(loop, 1, NULL); + pjsip_loop_set_failure(g[tid].loop, 1, NULL); PJ_LOG(5,(THIS_FILE, " transport loop fail mode set")); end_test = now; end_test.sec += 33; do { - pj_time_val interval = { 0, 1 }; + pj_time_val interval = { 0, 10 }; pj_gettimeofday(&now); pjsip_endpt_handle_events(endpt, &interval); - } while (!test_complete && PJ_TIME_VAL_LT(now, end_test)); + } while (!g[tid].test_complete && PJ_TIME_VAL_LT(now, end_test)); - if (test_complete != tests[i].result) { + if (g[tid].test_complete != tests[i].result) { PJ_LOG(3,(THIS_FILE, " error: expecting timeout")); return -41; } @@ -1564,98 +1741,99 @@ int tsx_transport_failure_test(void) ** ***************************************************************************** */ -int tsx_uas_test(struct tsx_test_param *param) +int tsx_uas_test(unsigned tid) { - pj_sockaddr_in addr; - pj_status_t status; - - test_param = param; - tp_flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)param->type); +#define ERR(rc__) { status=rc__; goto on_return; } + struct tsx_test_param *param = &tsx_test[tid]; + int status; - pj_ansi_snprintf(TARGET_URI, sizeof(TARGET_URI), "sip:bob@127.0.0.1:%d;transport=%s", - param->port, param->tp_type); - pj_ansi_snprintf(FROM_URI, sizeof(FROM_URI), "sip:alice@127.0.0.1:%d;transport=%s", - param->port, param->tp_type); + g[tid].test_param = param; + g[tid].tp_flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)param->type); - /* Check if loop transport is configured. */ - status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM, - &addr, sizeof(addr), NULL, &loop); - if (status != PJ_SUCCESS) { - PJ_LOG(3,(THIS_FILE, " Error: loop transport is not configured!")); - return -10; + /* Create loop transport */ + g[tid].loop = NULL; + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + PJ_TEST_SUCCESS(pjsip_loop_start(endpt, &g[tid].loop), + NULL, return -50); + pjsip_transport_add_ref(g[tid].loop); } + + pj_ansi_snprintf(g[tid].TARGET_URI, sizeof(g[tid].TARGET_URI), + "sip:%d@127.0.0.1:%d;transport=%s", + tid, param->port, param->tp_type); + pj_ansi_snprintf(g[tid].FROM_URI, sizeof(g[tid].FROM_URI), + "sip:tsx_uas_test@127.0.0.1:%d;transport=%s", + param->port, param->tp_type); + /* Register modules. */ - status = pjsip_endpt_register_module(endpt, &tsx_user); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to register module", status); - return -3; - } - status = pjsip_endpt_register_module(endpt, &msg_sender); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to register module", status); - return -4; + if ((status=register_modules(tid)) != PJ_SUCCESS) { + if (g[tid].loop) { + pjsip_transport_dec_ref(g[tid].loop); + g[tid].loop = NULL; + } + return -20; } /* TEST1_BRANCH_ID: Basic 2xx final response. * TEST2_BRANCH_ID: Basic non-2xx final response. */ - status = tsx_basic_final_response_test(); + status = tsx_basic_final_response_test(tid); if (status != 0) - return status; + goto on_return; /* TEST3_BRANCH_ID: with provisional response */ - status = tsx_basic_provisional_response_test(); + status = tsx_basic_provisional_response_test(tid); if (status != 0) - return status; + goto on_return; /* TEST4_BRANCH_ID: absorbs retransmissions in TRYING state */ - status = tsx_retransmit_last_response_test(TEST4_TITLE, + status = tsx_retransmit_last_response_test(tid, TEST4_TITLE, TEST4_BRANCH_ID, TEST4_REQUEST_COUNT, TEST4_STATUS_CODE); if (status != 0) - return status; + goto on_return; /* TEST5_BRANCH_ID: retransmit last response in PROCEEDING state */ - status = tsx_retransmit_last_response_test(TEST5_TITLE, + status = tsx_retransmit_last_response_test(tid, TEST5_TITLE, TEST5_BRANCH_ID, TEST5_REQUEST_COUNT, TEST5_STATUS_CODE); if (status != 0) - return status; + goto on_return; /* TEST6_BRANCH_ID: retransmit last response in COMPLETED state * This only applies to non-reliable transports, * since UAS transaction is destroyed as soon * as final response is sent for reliable transports. */ - if ((tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) { - status = tsx_retransmit_last_response_test(TEST6_TITLE, + if ((g[tid].tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) { + status = tsx_retransmit_last_response_test(tid, TEST6_TITLE, TEST6_BRANCH_ID, TEST6_REQUEST_COUNT, TEST6_STATUS_CODE); if (status != 0) - return status; + goto on_return; } /* TEST7_BRANCH_ID: INVITE non-2xx final response retransmission test * TEST8_BRANCH_ID: INVITE 2xx final response retransmission test */ - status = tsx_final_response_retransmission_test(); + status = tsx_final_response_retransmission_test(tid); if (status != 0) - return status; + goto on_return; /* TEST9_BRANCH_ID: retransmission of non-2xx INVITE final response must * cease when ACK is received * Only applicable for non-reliable transports. */ - if ((tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) { - status = tsx_ack_test(); + if ((g[tid].tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) { + status = tsx_ack_test(tid); if (status != 0) - return status; + goto on_return; } /* TEST10_BRANCH_ID: test transport failure in TRYING state. @@ -1665,27 +1843,22 @@ int tsx_uas_test(struct tsx_test_param *param) */ /* Only valid for loop-dgram */ if (param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { - status = tsx_transport_failure_test(); + status = tsx_transport_failure_test(tid); if (status != 0) - return status; + goto on_return; } + status = 0; - /* Register modules. */ - status = pjsip_endpt_unregister_module(endpt, &tsx_user); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to unregister module", status); - return -8; - } - status = pjsip_endpt_unregister_module(endpt, &msg_sender); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to unregister module", status); - return -9; - } - +on_return: - if (loop) - pjsip_transport_dec_ref(loop); + if (g[tid].loop) { + /* Order must be shutdown then dec_ref so it gets destroyed */ + pjsip_transport_shutdown(g[tid].loop); + pjsip_transport_dec_ref(g[tid].loop); + g[tid].loop = NULL; + } - return 0; + unregister_modules(tid); + return status; } diff --git a/pjsip/src/test/txdata_test.c b/pjsip/src/test/txdata_test.c index ee0ce4d47f..b06f9b8615 100644 --- a/pjsip/src/test/txdata_test.c +++ b/pjsip/src/test/txdata_test.c @@ -636,7 +636,7 @@ static int create_request_bench(pj_timestamp *p_elapsed) pj_status_t status; pj_str_t str_target = pj_str("sip:someuser@someprovider.com"); - pj_str_t str_from = pj_str("\"Local User\" "); + pj_str_t str_from = pj_str("\"Local User\" "); pj_str_t str_to = pj_str("\"Remote User\" "); pj_str_t str_contact = str_from; @@ -695,7 +695,7 @@ static int create_response_bench(pj_timestamp *p_elapsed) /* Create the request first. */ pj_str_t str_target = pj_str("sip:someuser@someprovider.com"); - pj_str_t str_from = pj_str("\"Local User\" "); + pj_str_t str_from = pj_str("\"Local User\" "); pj_str_t str_to = pj_str("\"Remote User\" "); pj_str_t str_contact = str_from; diff --git a/tests/pjsua/runall.py b/tests/pjsua/runall.py index c0b0231dc4..7648f8bd3f 100644 --- a/tests/pjsua/runall.py +++ b/tests/pjsua/runall.py @@ -222,7 +222,7 @@ if (i < retry_num + 1): continue if with_log: - lines = open(logname, "r", encoding='utf-8').readlines() + lines = open(logname, "r", encoding='utf-8', errors='ignore').readlines() print(''.join(lines)) print("Log file: '" + logname + "'.") fails_cnt += 1 diff --git a/third_party/BaseClasses/renbase.cpp b/third_party/BaseClasses/renbase.cpp index b354b5fb6d..8ea70c953f 100644 --- a/third_party/BaseClasses/renbase.cpp +++ b/third_party/BaseClasses/renbase.cpp @@ -17,6 +17,10 @@ #pragma warning(disable:4355) +#if defined(_MSC_VER) +# pragma comment(lib, "winmm.lib") +#endif + // Helper function for clamping time differences int inline TimeDiff(REFERENCE_TIME rt) { From 6853491ade9b816b948c09ffe87c9e3aad5a5719 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Fri, 10 Jan 2025 10:58:44 +0700 Subject: [PATCH 156/491] Add helper APIs for adding & removing port destroy handler (#4244) --- pjmedia/include/pjmedia/conference.h | 45 +++++++++++++++++++ pjmedia/include/pjmedia/port.h | 54 +++++++++++++++++++++- pjmedia/include/pjmedia/vid_conf.h | 49 ++++++++++++++++++-- pjmedia/src/pjmedia/conf_switch.c | 37 +++++++++++++++ pjmedia/src/pjmedia/conference.c | 67 ++++++++++++++++++++++++++-- pjmedia/src/pjmedia/port.c | 30 +++++++++++++ pjmedia/src/pjmedia/vid_conf.c | 62 +++++++++++++++++++++++++ 7 files changed, 336 insertions(+), 8 deletions(-) diff --git a/pjmedia/include/pjmedia/conference.h b/pjmedia/include/pjmedia/conference.h index 9000daaa9f..e0a273e6e3 100644 --- a/pjmedia/include/pjmedia/conference.h +++ b/pjmedia/include/pjmedia/conference.h @@ -425,6 +425,10 @@ PJ_DECL(unsigned) pjmedia_conf_get_connect_count(pjmedia_conf *conf); * should not assume that the port will no longer receive/send audio frame * after this function has returned. * + * If the port uses any app's resources, application should maintain + * the resources validity until the port is completely removed. Application + * can schedule the resource release via #pjmedia_conf_add_destroy_handler(). + * * @param conf The conference bridge. * @param slot The port index to be removed. * @@ -602,6 +606,47 @@ PJ_DECL(pj_status_t) pjmedia_conf_adjust_conn_level( pjmedia_conf *conf, int adj_level ); +/** + * Add port destructor handler. + * + * Application can use this function to schedule resource release. + * Note that application cannot release any app's resources used by the port, + * e.g: memory pool, database connection, immediately after removing the port + * from the conference bridge as port removal is asynchronous. + * + * Usually this function is called after adding the port to the conference + * bridge. + * + * @param conf The conference bridge. + * @param slot The port slot index. + * @param member A pointer to be passed to the handler. + * @param handler The destroy handler. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_conf_add_destroy_handler( + pjmedia_conf* conf, + unsigned slot, + void* member, + pj_grp_lock_handler handler); + + +/** + * Remove previously registered destructor handler. + * + * @param conf The conference bridge. + * @param slot The port slot index. + * @param member A pointer to be passed to the handler. + * @param handler The destroy handler. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_conf_del_destroy_handler( + pjmedia_conf* conf, + unsigned slot, + void* member, + pj_grp_lock_handler handler); + PJ_END_DECL diff --git a/pjmedia/include/pjmedia/port.h b/pjmedia/include/pjmedia/port.h index 20b1c0e43b..86e3b2ab07 100644 --- a/pjmedia/include/pjmedia/port.h +++ b/pjmedia/include/pjmedia/port.h @@ -526,8 +526,18 @@ PJ_DECL(pj_status_t) pjmedia_port_destroy( pjmedia_port *port ); * lock by adding the port's destructor to the group lock handler list, and * increment the reference counter. * - * This function should only be called by media port implementation. The port - * must have its own pool which will be released in its on_destroy() function. + * Normally this function is only called by media port implementation, + * application should never call this function. + * + * This function will check whether the port implements on_destroy(), + * because when working with conference bridge, a port is required to manage + * its lifetime via group lock (e.g: initialized by this function), + * so basically it must use its own pool and the pool is normally + * released from its on_destroy(). + * + * Also note that this function will add a group lock destroy handler + * that calls port's on_destroy(), so port implementation can release + * its resources from on_destroy() as usual. * * @param port The pjmedia port to be initialized. * @param pool The pool, this can be a temporary pool as @@ -563,6 +573,46 @@ PJ_DECL(pj_status_t) pjmedia_port_add_ref( pjmedia_port *port ); PJ_DECL(pj_status_t) pjmedia_port_dec_ref( pjmedia_port *port ); +/** + * This is a helper function to add port destructor handler. + * + * Application can use this function to schedule its resource release. + * Note that application cannot release the resources related to the port, + * e.g: memory pool (custom ports that do not use its own pool), + * immediately after removing the port from the conference bridge + * as the port removal is done asynchronously. + * + * Usually this function is called after adding the port to the conference + * bridge. + * + * @param port The PJMEDIA port. + * @param member A pointer to be passed to the handler. + * @param handler The destroy handler. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_port_add_destroy_handler( + pjmedia_port* port, + void *member, + pj_grp_lock_handler handler); + + +/** + * This is a helper function to remove previously registered + * destructor handler. + * + * @param port The PJMEDIA port. + * @param member A pointer to be passed to the handler. + * @param handler The destroy handler. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_port_del_destroy_handler( + pjmedia_port* port, + void *member, + pj_grp_lock_handler handler); + + PJ_END_DECL /** diff --git a/pjmedia/include/pjmedia/vid_conf.h b/pjmedia/include/pjmedia/vid_conf.h index 30c6a34e97..4cb278af35 100644 --- a/pjmedia/include/pjmedia/vid_conf.h +++ b/pjmedia/include/pjmedia/vid_conf.h @@ -221,7 +221,7 @@ PJ_DECL(unsigned) pjmedia_vid_conf_get_port_count(pjmedia_vid_conf *vid_conf); /** * Enumerate occupied slots in the video conference bridge. * - * @param vid_conf The video conference bridge. + * @param vid_conf The video conference bridge. * @param slots Array of slot to be filled in. * @param count On input, specifies the maximum number of slot * in the array. On return, it will be filled with @@ -253,7 +253,7 @@ PJ_DECL(pj_status_t) pjmedia_vid_conf_get_port_info( * Enable unidirectional video flow from the specified source slot to * the specified sink slot. * - * @param vid_conf The video conference bridge. + * @param vid_conf The video conference bridge. * @param src_slot Source slot. * @param sink_slot Sink slot. * @param opt The option, for future use, currently this must @@ -272,7 +272,7 @@ PJ_DECL(pj_status_t) pjmedia_vid_conf_connect_port( * Disconnect unidirectional video flow from the specified source to * the specified sink slot. * - * @param vid_conf The video conference bridge. + * @param vid_conf The video conference bridge. * @param src_slot Source slot. * @param sink_slot Sink slot. * @@ -301,6 +301,49 @@ PJ_DECL(pj_status_t) pjmedia_vid_conf_update_port(pjmedia_vid_conf *vid_conf, unsigned slot); + +/** + * Add port destructor handler. + * + * Application can use this function to schedule resource release. + * Note that application cannot release any app's resources used by the port, + * e.g: memory pool, database connection, immediately after removing the port + * from the conference bridge as port removal is asynchronous. + * + * Usually this function is called after adding the port to the conference + * bridge. + * + * @param vid_conf The video conference bridge. + * @param slot The port slot index. + * @param member A pointer to be passed to the handler. + * @param handler The destroy handler. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_vid_conf_add_destroy_handler( + pjmedia_vid_conf* vid_conf, + unsigned slot, + void* member, + pj_grp_lock_handler handler); + + +/** + * Remove previously registered destructor handler. + * + * @param conf The video conference bridge. + * @param slot The port slot index. + * @param member A pointer to be passed to the handler. + * @param handler The destroy handler. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_vid_conf_del_destroy_handler( + pjmedia_vid_conf* vid_conf, + unsigned slot, + void* member, + pj_grp_lock_handler handler); + + PJ_END_DECL /** diff --git a/pjmedia/src/pjmedia/conf_switch.c b/pjmedia/src/pjmedia/conf_switch.c index 74a3ffd7c1..90f1739cef 100644 --- a/pjmedia/src/pjmedia/conf_switch.c +++ b/pjmedia/src/pjmedia/conf_switch.c @@ -1585,4 +1585,41 @@ static pj_status_t put_frame(pjmedia_port *this_port, return PJ_SUCCESS; } + +/* + * Add destructor handler. + */ +PJ_DEF(pj_status_t) pjmedia_conf_add_destroy_handler( + pjmedia_conf* conf, + unsigned slot, + void* member, + pj_grp_lock_handler handler) +{ + PJ_UNUSED_ARG(conf); + PJ_UNUSED_ARG(slot); + PJ_UNUSED_ARG(member); + PJ_UNUSED_ARG(handler); + + return PJ_ENOTSUP; +} + + +/* + * Remove previously registered destructor handler. + */ +PJ_DEF(pj_status_t) pjmedia_conf_del_destroy_handler( + pjmedia_conf* conf, + unsigned slot, + void* member, + pj_grp_lock_handler handler) +{ + PJ_UNUSED_ARG(conf); + PJ_UNUSED_ARG(slot); + PJ_UNUSED_ARG(member); + PJ_UNUSED_ARG(handler); + + return PJ_ENOTSUP; +} + + #endif diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c index 0e6277a219..12f16eaac9 100644 --- a/pjmedia/src/pjmedia/conference.c +++ b/pjmedia/src/pjmedia/conference.c @@ -1413,11 +1413,11 @@ static void op_disconnect_ports(pjmedia_conf *conf, /* Disconnect source -> sink */ if (src_port && dst_port) { /* Check if connection has been made */ - for (i=0; ilistener_cnt; ++i) { + for (i=0; i<(int)src_port->listener_cnt; ++i) { if (src_port->listener_slots[i] == sink_slot) break; } - if (i == src_port->listener_cnt) { + if (i == (int)src_port->listener_cnt) { PJ_LOG(3,(THIS_FILE, "Ports connection %d->%d does not exist", src_slot, sink_slot)); return; @@ -1458,7 +1458,7 @@ static void op_disconnect_ports(pjmedia_conf *conf, (int)dst_port->name.slen, dst_port->name.ptr)); - for (i=0; imax_ports; ++i) { + for (i=0; i<(int)conf->max_ports; ++i) { int j; src_port = conf->ports[i]; @@ -2806,4 +2806,65 @@ static pj_status_t put_frame(pjmedia_port *this_port, return status; } + +/* + * Add destructor handler. + */ +PJ_DEF(pj_status_t) pjmedia_conf_add_destroy_handler( + pjmedia_conf* conf, + unsigned slot, + void* member, + pj_grp_lock_handler handler) +{ + struct conf_port *cport; + pj_grp_lock_t *grp_lock; + + PJ_ASSERT_RETURN(conf && handler && slot < conf->max_ports, PJ_EINVAL); + + pj_mutex_lock(conf->mutex); + + /* Port must be valid and has group lock. */ + cport = conf->ports[slot]; + if (!cport || !cport->port || !cport->port->grp_lock) { + pj_mutex_unlock(conf->mutex); + return cport? PJ_EINVALIDOP : PJ_EINVAL; + } + grp_lock = cport->port->grp_lock; + + pj_mutex_unlock(conf->mutex); + + return pj_grp_lock_add_handler(grp_lock, NULL, member, handler); +} + + +/* + * Remove previously registered destructor handler. + */ +PJ_DEF(pj_status_t) pjmedia_conf_del_destroy_handler( + pjmedia_conf* conf, + unsigned slot, + void* member, + pj_grp_lock_handler handler) +{ + struct conf_port* cport; + pj_grp_lock_t* grp_lock; + + PJ_ASSERT_RETURN(conf && handler && slot < conf->max_ports, PJ_EINVAL); + + pj_mutex_lock(conf->mutex); + + /* Port must be valid and has group lock. */ + cport = conf->ports[slot]; + if (!cport || !cport->port || !cport->port->grp_lock) { + pj_mutex_unlock(conf->mutex); + return cport ? PJ_EINVALIDOP : PJ_EINVAL; + } + grp_lock = cport->port->grp_lock; + + pj_mutex_unlock(conf->mutex); + + return pj_grp_lock_del_handler(grp_lock, member, handler); +} + + #endif diff --git a/pjmedia/src/pjmedia/port.c b/pjmedia/src/pjmedia/port.c index 2286fd0439..1331c90e7a 100644 --- a/pjmedia/src/pjmedia/port.c +++ b/pjmedia/src/pjmedia/port.c @@ -232,3 +232,33 @@ PJ_DEF(pj_status_t) pjmedia_port_dec_ref( pjmedia_port *port ) return pj_grp_lock_dec_ref(port->grp_lock); } + + +/** + * Add destructor handler. + */ +PJ_DEF(pj_status_t) pjmedia_port_add_destroy_handler( + pjmedia_port* port, + void* member, + pj_grp_lock_handler handler) +{ + PJ_ASSERT_RETURN(port && handler, PJ_EINVAL); + PJ_ASSERT_RETURN(port->grp_lock, PJ_EINVALIDOP); + + return pj_grp_lock_add_handler(port->grp_lock, NULL, member, handler); +} + + +/** + * Remove previously registered destructor handler. + */ +PJ_DEF(pj_status_t) pjmedia_port_del_destroy_handler( + pjmedia_port* port, + void* member, + pj_grp_lock_handler handler) +{ + PJ_ASSERT_RETURN(port && handler, PJ_EINVAL); + PJ_ASSERT_RETURN(port->grp_lock, PJ_EINVALIDOP); + + return pj_grp_lock_del_handler(port->grp_lock, member, handler); +} diff --git a/pjmedia/src/pjmedia/vid_conf.c b/pjmedia/src/pjmedia/vid_conf.c index 81ae52501c..5cf813c88b 100644 --- a/pjmedia/src/pjmedia/vid_conf.c +++ b/pjmedia/src/pjmedia/vid_conf.c @@ -1733,4 +1733,66 @@ static void op_update_port(pjmedia_vid_conf *vid_conf, } +/* + * Add destructor handler. + */ +PJ_DEF(pj_status_t) pjmedia_vid_conf_add_destroy_handler( + pjmedia_vid_conf* vid_conf, + unsigned slot, + void* member, + pj_grp_lock_handler handler) +{ + struct vconf_port* cport; + pj_grp_lock_t* grp_lock; + + PJ_ASSERT_RETURN(vid_conf && handler && slot < vid_conf->opt.max_slot_cnt, + PJ_EINVAL); + + pj_mutex_lock(vid_conf->mutex); + + /* Port must be valid and has group lock. */ + cport = vid_conf->ports[slot]; + if (!cport || !cport->port || !cport->port->grp_lock) { + pj_mutex_unlock(vid_conf->mutex); + return cport ? PJ_EINVALIDOP : PJ_EINVAL; + } + grp_lock = cport->port->grp_lock; + + pj_mutex_unlock(vid_conf->mutex); + + return pj_grp_lock_add_handler(grp_lock, NULL, member, handler); +} + + +/* + * Remove previously registered destructor handler. + */ +PJ_DEF(pj_status_t) pjmedia_vid_conf_del_destroy_handler( + pjmedia_vid_conf* vid_conf, + unsigned slot, + void* member, + pj_grp_lock_handler handler) +{ + struct vconf_port* cport; + pj_grp_lock_t* grp_lock; + + PJ_ASSERT_RETURN(vid_conf && handler && slot < vid_conf->opt.max_slot_cnt, + PJ_EINVAL); + + pj_mutex_lock(vid_conf->mutex); + + /* Port must be valid and has group lock. */ + cport = vid_conf->ports[slot]; + if (!cport || !cport->port || !cport->port->grp_lock) { + pj_mutex_unlock(vid_conf->mutex); + return cport ? PJ_EINVALIDOP : PJ_EINVAL; + } + grp_lock = cport->port->grp_lock; + + pj_mutex_unlock(vid_conf->mutex); + + return pj_grp_lock_del_handler(grp_lock, member, handler); +} + + #endif /* PJMEDIA_HAS_VIDEO */ From 2d4b94a74e2738a174aa7c4cc2fe022620b67132 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Fri, 10 Jan 2025 18:49:42 +0900 Subject: [PATCH 157/491] CI: change sleep_test() and timestamp_test() to exclusive on Windows (#4246) * Set sleep_test() as exclusive on Windows. Also set exclusive tests to run last for slightly more efficient testing time * Also changed timestamp_test() to exclusive as well because it often drifts on GH Windows CI --- pjlib/src/pjlib-test/test.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c index 055f8a2c9b..c8cfe60835 100644 --- a/pjlib/src/pjlib-test/test.c +++ b/pjlib/src/pjlib-test/test.c @@ -242,8 +242,18 @@ static int features_tests(int argc, char *argv[]) UT_ADD_TEST(&test_app.ut_app, hash_test, 0); #endif + /* Windows GH CI oftent fails with: + + 07:27:13.217 ...testing frequency accuracy (pls wait) + 07:27:23.440 ....error: timestamp drifted by 3800 usec after 10020 msec + */ #if INCLUDE_TIMESTAMP_TEST +# if defined(PJ_WIN32) && PJ_WIN32!=0 + UT_ADD_TEST(&test_app.ut_app, timestamp_test, + PJ_TEST_EXCLUSIVE | PJ_TEST_KEEP_LAST); +# else UT_ADD_TEST(&test_app.ut_app, timestamp_test, 0); +# endif #endif #if INCLUDE_ATOMIC_TEST @@ -254,8 +264,17 @@ static int features_tests(int argc, char *argv[]) UT_ADD_TEST(&test_app.ut_app, timer_test, 0); #endif + /* Very often sleep test failed on GitHub Windows CI, with + the thread sleeping for much longer than tolerated. So + as a workaround, set it as exclusive. + */ #if INCLUDE_SLEEP_TEST +# if defined(PJ_WIN32) && PJ_WIN32!=0 + UT_ADD_TEST(&test_app.ut_app, sleep_test, + PJ_TEST_EXCLUSIVE | PJ_TEST_KEEP_LAST); +# else UT_ADD_TEST(&test_app.ut_app, sleep_test, 0); +# endif #endif #if INCLUDE_FILE_TEST @@ -299,7 +318,8 @@ static int features_tests(int argc, char *argv[]) */ #if INCLUDE_IOQUEUE_STRESS_TEST # if defined(PJ_WIN32) && PJ_WIN32!=0 - UT_ADD_TEST(&test_app.ut_app, ioqueue_stress_test, PJ_TEST_EXCLUSIVE); + UT_ADD_TEST(&test_app.ut_app, ioqueue_stress_test, + PJ_TEST_EXCLUSIVE | PJ_TEST_KEEP_LAST); # else UT_ADD_TEST(&test_app.ut_app, ioqueue_stress_test, 0); # endif From b955e8f925c3a932d07fe6d21b23ef33703b0ab8 Mon Sep 17 00:00:00 2001 From: sauwming Date: Mon, 13 Jan 2025 16:00:34 +0800 Subject: [PATCH 158/491] Fixed memory leak in mips test (#4247) --- pjmedia/src/test/mips_test.c | 70 ++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 14 deletions(-) diff --git a/pjmedia/src/test/mips_test.c b/pjmedia/src/test/mips_test.c index 69391787b1..c2a2e93e74 100644 --- a/pjmedia/src/test/mips_test.c +++ b/pjmedia/src/test/mips_test.c @@ -402,8 +402,8 @@ struct test_entry void (*custom_run)(struct test_entry*); void (*custom_deinit)(struct test_entry*); - void *pdata[4]; - unsigned idata[4]; + void *pdata[2*16+1]; +// unsigned idata[4]; }; @@ -465,6 +465,22 @@ static pjmedia_port* gen_port_test_init(pj_pool_t *pool, /***************************************************************************/ + +static void conf_port_custom_deinit(struct test_entry *te) +{ + unsigned i; + pjmedia_conf *conf = (pjmedia_conf *)te->pdata[0]; + + if (conf) + pjmedia_conf_destroy(conf); + + for (i = 1; i < PJ_ARRAY_SIZE(te->pdata); i++) { + if (!te->pdata[i]) + break; + pjmedia_port_destroy((pjmedia_port *)te->pdata[i]); + } +} + static pjmedia_port* init_conf_port(unsigned nb_participant, pj_pool_t *pool, unsigned clock_rate, @@ -474,11 +490,13 @@ static pjmedia_port* init_conf_port(unsigned nb_participant, struct test_entry *te) { pjmedia_conf *conf; - unsigned i; + unsigned i, nport = 1; pj_status_t status; PJ_UNUSED_ARG(flags); - PJ_UNUSED_ARG(te); + + te->custom_deinit = &conf_port_custom_deinit; + pj_bzero(te->pdata, sizeof(te->pdata)); /* Create conf */ status = pjmedia_conf_create(pool, 2+nb_participant*2, clock_rate, @@ -486,6 +504,7 @@ static pjmedia_port* init_conf_port(unsigned nb_participant, PJMEDIA_CONF_NO_DEVICE, &conf); if (status != PJ_SUCCESS) return NULL; + te->pdata[0] = conf; for (i=0; ipdata[nport++] = gen_port; /* Add port */ status = pjmedia_conf_add_port(conf, pool, gen_port, NULL, &slot1); @@ -512,6 +532,7 @@ static pjmedia_port* init_conf_port(unsigned nb_participant, samples_per_frame, 16, &null_port); if (status != PJ_SUCCESS) return NULL; + te->pdata[nport++] = null_port; /* add null port */ status = pjmedia_conf_add_port(conf, pool, null_port, NULL, &slot2); @@ -727,9 +748,11 @@ static pj_status_t codec_on_destroy(struct pjmedia_port *this_port) { struct codec_port *cp = (struct codec_port*)this_port; - pjmedia_codec_close(cp->codec); - pjmedia_codec_mgr_dealloc_codec(pjmedia_endpt_get_codec_mgr(cp->endpt), - cp->codec); + if (cp->codec) { + pjmedia_codec_close(cp->codec); + pjmedia_codec_mgr_dealloc_codec(pjmedia_endpt_get_codec_mgr(cp->endpt), + cp->codec); + } cp->codec_deinit(); pjmedia_endpt_destroy2(cp->endpt); return PJ_SUCCESS; @@ -770,33 +793,37 @@ static pjmedia_port* codec_encode_decode( pj_pool_t *pool, status = codec_init(cp->endpt); if (status != PJ_SUCCESS) - return NULL; + goto on_error; count = 1; status = pjmedia_codec_mgr_find_codecs_by_id(pjmedia_endpt_get_codec_mgr(cp->endpt), &codec_id, &count, ci, NULL); if (status != PJ_SUCCESS) - return NULL; + goto on_error; status = pjmedia_codec_mgr_alloc_codec(pjmedia_endpt_get_codec_mgr(cp->endpt), ci[0], &cp->codec); if (status != PJ_SUCCESS) - return NULL; + goto on_error; status = pjmedia_codec_mgr_get_default_param(pjmedia_endpt_get_codec_mgr(cp->endpt), ci[0], &codec_param); if (status != PJ_SUCCESS) - return NULL; + goto on_error; status = pjmedia_codec_init(cp->codec, pool); if (status != PJ_SUCCESS) - return NULL; + goto on_error; status = pjmedia_codec_open(cp->codec, &codec_param); if (status != PJ_SUCCESS) - return NULL; + goto on_error; return &cp->base; + +on_error: + codec_on_destroy(&cp->base); + return NULL; } #endif @@ -1346,6 +1373,16 @@ static pjmedia_port* wsola_discard_50(pj_pool_t *pool, /***************************************************************************/ +static void ec_custom_deinit(struct test_entry *te) +{ + unsigned i; + + for (i = 0; i < PJ_ARRAY_SIZE(te->pdata); i++) { + if (!te->pdata[i]) break; + pjmedia_port_destroy((pjmedia_port *)te->pdata[i]); + } +} + static pjmedia_port* ec_create(unsigned ec_tail_msec, pj_pool_t *pool, unsigned clock_rate, @@ -1357,17 +1394,20 @@ static pjmedia_port* ec_create(unsigned ec_tail_msec, pjmedia_port *gen_port, *ec_port; pj_status_t status; - PJ_UNUSED_ARG(te); + te->custom_deinit = &ec_custom_deinit; + pj_bzero(te->pdata, sizeof(te->pdata)); gen_port = create_gen_port(pool, clock_rate, channel_count, samples_per_frame, 100); if (gen_port == NULL) return NULL; + te->pdata[0] = gen_port; status = pjmedia_echo_port_create(pool, gen_port, ec_tail_msec, 0, flags, &ec_port); if (status != PJ_SUCCESS) return NULL; + te->pdata[1] = ec_port; return ec_port; } @@ -2386,6 +2426,8 @@ static pj_timestamp run_entry(unsigned clock_rate, struct test_entry *e) pj_sub_timestamp(&t1, &t0); + pjmedia_port_destroy(gen_port); + if (e->custom_deinit) e->custom_deinit(e); else From 90293ccc52ea16c5d0e2f5c81768a59b4fc10481 Mon Sep 17 00:00:00 2001 From: sauwming Date: Mon, 13 Jan 2025 21:47:33 +0800 Subject: [PATCH 159/491] Fixed various Coverity warnings (#4249) --- pjlib/src/pjlib-test/test.c | 1 + pjmedia/src/test/vid_codec_test.c | 6 ++++-- pjnath/src/pjnath-test/test.c | 7 +++++-- pjsip/src/pjsip-ua/sip_siprec.c | 1 - pjsip/src/test/transport_loop_test.c | 4 ++-- pjsip/src/test/tsx_bench.c | 3 +-- pjsip/src/test/tsx_uas_test.c | 4 +++- pjsip/src/test/uri_test.c | 12 +++++++----- 8 files changed, 23 insertions(+), 15 deletions(-) diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c index c8cfe60835..ef24678f34 100644 --- a/pjlib/src/pjlib-test/test.c +++ b/pjlib/src/pjlib-test/test.c @@ -366,6 +366,7 @@ int test_inner(int argc, char *argv[]) pj_log_set_level(3); pj_log_set_decor(test_app.param_log_decor); + pj_bzero(&stat, sizeof(pj_test_stat)); rc = pj_init(); if (rc != 0) { diff --git a/pjmedia/src/test/vid_codec_test.c b/pjmedia/src/test/vid_codec_test.c index 4d2c2402df..96538f78d5 100644 --- a/pjmedia/src/test/vid_codec_test.c +++ b/pjmedia/src/test/vid_codec_test.c @@ -276,8 +276,10 @@ static int encode_decode_test(pj_pool_t *pool, const char *codec_id, pjmedia_vid_dev_info cdi; status = pjmedia_vid_dev_get_info(i, &cdi); - if (status != PJ_SUCCESS) - rc = 211; goto on_return; + if (status != PJ_SUCCESS) { + rc = 211; + goto on_return; + } /* Only interested with render device */ if ((cdi.dir & PJMEDIA_DIR_RENDER) != 0) { diff --git a/pjnath/src/pjnath-test/test.c b/pjnath/src/pjnath-test/test.c index 41130524fb..07125d5f6d 100644 --- a/pjnath/src/pjnath-test/test.c +++ b/pjnath/src/pjnath-test/test.c @@ -64,9 +64,9 @@ pj_status_t create_stun_config(app_sess_t *app_sess) { status=PJ_ENOMEM; goto on_error;}); PJ_TEST_SUCCESS(pj_ioqueue_create(app_sess->pool, 64, &ioqueue), NULL, - goto on_error); + {status = tmp_status_; goto on_error;}); PJ_TEST_SUCCESS(pj_timer_heap_create(app_sess->pool, 256, &timer_heap), - NULL, goto on_error); + NULL, {status = tmp_status_; goto on_error;}); pj_lock_create_recursive_mutex(app_sess->pool, NULL, &lock); pj_timer_heap_set_lock(timer_heap, lock, PJ_TRUE); @@ -82,8 +82,11 @@ pj_status_t create_stun_config(app_sess_t *app_sess) pj_ioqueue_destroy(ioqueue); if (timer_heap) pj_timer_heap_destroy(timer_heap); + // Lock should have been destroyed by timer heap. + /* if (lock) pj_lock_destroy(lock); + */ if (app_sess->pool) pj_pool_release(app_sess->pool); pj_caching_pool_destroy(&app_sess->cp); diff --git a/pjsip/src/pjsip-ua/sip_siprec.c b/pjsip/src/pjsip-ua/sip_siprec.c index 124b714bf1..96d4b69497 100644 --- a/pjsip/src/pjsip-ua/sip_siprec.c +++ b/pjsip/src/pjsip-ua/sip_siprec.c @@ -140,7 +140,6 @@ PJ_DEF(pj_status_t) pjsip_siprec_verify_request(pjsip_rx_data *rdata, pj_status_t status = PJ_SUCCESS; const char *warn_text = NULL; pjsip_hdr res_hdr_list; - pjsip_param *param; /* Init return arguments. */ if (p_tdata) *p_tdata = NULL; diff --git a/pjsip/src/test/transport_loop_test.c b/pjsip/src/test/transport_loop_test.c index dbd6f79b28..fb7f89e159 100644 --- a/pjsip/src/test/transport_loop_test.c +++ b/pjsip/src/test/transport_loop_test.c @@ -103,10 +103,11 @@ int transport_loop_multi_test(void) pjsip_transport *loops[N]; int i, rc; + pj_bzero(loops, sizeof(loops)); + PJ_TEST_SUCCESS(pjsip_endpt_register_module(endpt, &loop_tester_mod), NULL, ERR(-5)); - pj_bzero(loops, sizeof(loops)); for (i=0; i Date: Tue, 14 Jan 2025 10:17:11 +0800 Subject: [PATCH 160/491] Fixed pjsua2 unnecessary object copy (#4248) --- pjsip-apps/src/samples/pjsua2_demo.cpp | 16 +++++++++------- pjsip/include/pjsua2/config.hpp | 10 ++++++++++ pjsip/include/pjsua2/presence.hpp | 3 ++- pjsip/src/pjsua2/account.cpp | 16 ++++++++-------- pjsip/src/pjsua2/endpoint.cpp | 8 ++++---- pjsip/src/pjsua2/media.cpp | 12 ++++++------ pjsip/src/pjsua2/siptypes.cpp | 14 +++++++------- 7 files changed, 46 insertions(+), 33 deletions(-) diff --git a/pjsip-apps/src/samples/pjsua2_demo.cpp b/pjsip-apps/src/samples/pjsua2_demo.cpp index 1ce27cecd8..2aa30be7d5 100644 --- a/pjsip-apps/src/samples/pjsua2_demo.cpp +++ b/pjsip-apps/src/samples/pjsua2_demo.cpp @@ -177,6 +177,8 @@ void MyCall::onCallMediaState(OnCallMediaStateParam &prm) AudioMedia& play_dev_med = MyEndpoint::instance().audDevManager().getPlaybackDevMedia(); + std::cout << "Getting playback dev media" << std::endl; + try { // Get the first audio media aud_med = getAudioMedia(-1); @@ -240,7 +242,7 @@ static void mainProg1(MyEndpoint &ep) // Transport TransportConfig tcfg; - tcfg.port = 5060; + tcfg.port = 6000; ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg); // Start library @@ -249,17 +251,17 @@ static void mainProg1(MyEndpoint &ep) // Add account AccountConfig acc_cfg; - acc_cfg.idUri = "sip:test1@pjsip.org"; + acc_cfg.idUri = "sip:401@pjsip.org"; acc_cfg.regConfig.registrarUri = "sip:sip.pjsip.org"; #if PJSIP_HAS_DIGEST_AKA_AUTH AuthCredInfo aci("Digest", "*", "test", PJSIP_CRED_DATA_EXT_AKA | PJSIP_CRED_DATA_PLAIN_PASSWD, "passwd"); aci.akaK = "passwd"; #else - AuthCredInfo aci("digest", "*", "test1", 0, "test1"); + AuthCredInfo aci("digest", "*", "401", 0, "pw401"); #endif - acc_cfg.sipConfig.authCreds.push_back(aci); + acc_cfg.sipConfig.authCreds.push_back(PJSUA2_MOVE(aci)); MyAccount *acc(new MyAccount); try { acc->create(acc_cfg); @@ -275,7 +277,7 @@ static void mainProg1(MyEndpoint &ep) CallOpParam prm(true); prm.opt.audioCount = 1; prm.opt.videoCount = 0; - call->makeCall("sip:test1@pjsip.org", prm); + call->makeCall("sip:402@sip.pjsip.org", prm); // Hangup all calls pj_thread_sleep(4000); @@ -412,7 +414,7 @@ static void mainProg(MyEndpoint &) SipHeader h; h.hName = "X-Header"; h.hValue = "User header"; - accCfg.regConfig.headers.push_back(h); + accCfg.regConfig.headers.push_back(PJSUA2_MOVE(h)); accCfg.sipConfig.proxies.push_back(""); accCfg.sipConfig.proxies.push_back(""); @@ -431,7 +433,7 @@ static void mainProg(MyEndpoint &) aci.dataType |= PJSIP_CRED_DATA_EXT_AKA; aci.akaK = "key"; #endif - accCfg.sipConfig.authCreds.push_back(aci); + accCfg.sipConfig.authCreds.push_back(PJSUA2_MOVE(aci)); jdoc.writeObject(accCfg); json_str = jdoc.saveString(); diff --git a/pjsip/include/pjsua2/config.hpp b/pjsip/include/pjsua2/config.hpp index 2a29921cd6..36d327136d 100644 --- a/pjsip/include/pjsua2/config.hpp +++ b/pjsip/include/pjsua2/config.hpp @@ -65,6 +65,16 @@ # define PJSUA2_THROW(x) throw(x) #endif +/** + * std::move() is only supported from C++11. + */ +#if (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L) || \ + (__cplusplus >= 201103L) + #define PJSUA2_MOVE(x) std::move(x) +#else + #define PJSUA2_MOVE(x) (x) +#endif + /** * @} PJSUA2_CFG */ diff --git a/pjsip/include/pjsua2/presence.hpp b/pjsip/include/pjsua2/presence.hpp index d19cd66e53..0145eed0ab 100644 --- a/pjsip/include/pjsua2/presence.hpp +++ b/pjsip/include/pjsua2/presence.hpp @@ -179,7 +179,8 @@ struct BuddyInfo * Default constructor */ BuddyInfo() - : presMonitorEnabled(true), + : accId(PJSUA_INVALID_ID), + presMonitorEnabled(true), subState(PJSIP_EVSUB_STATE_UNKNOWN), subTermCode(PJSIP_SC_NULL) {} diff --git a/pjsip/src/pjsua2/account.cpp b/pjsip/src/pjsua2/account.cpp index 8968e1ee0a..a8af6a1e4a 100644 --- a/pjsip/src/pjsua2/account.cpp +++ b/pjsip/src/pjsua2/account.cpp @@ -65,7 +65,7 @@ void RtcpFbConfig::fromPj(const pjmedia_rtcp_fb_setting &prm) for (unsigned i = 0; i < prm.cap_count; ++i) { RtcpFbCap cap; cap.fromPj(prm.caps[i]); - this->caps.push_back(cap); + this->caps.push_back(PJSUA2_MOVE(cap)); } } @@ -96,7 +96,7 @@ void RtcpFbConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error) NODE_READ_NUM_T (cap_node, pjmedia_rtcp_fb_type, cap.type); NODE_READ_STRING (cap_node, cap.typeName); NODE_READ_STRING (cap_node, cap.param); - this->caps.push_back(cap); + this->caps.push_back(PJSUA2_MOVE(cap)); } } @@ -157,7 +157,7 @@ void SrtpOpt::fromPj(const pjsua_srtp_opt &prm) for (unsigned i = 0; i < prm.crypto_count; ++i) { SrtpCrypto crypto; crypto.fromPj(prm.crypto[i]); - this->cryptos.push_back(crypto); + this->cryptos.push_back(PJSUA2_MOVE(crypto)); } this->keyings.clear(); @@ -196,7 +196,7 @@ void SrtpOpt::readObject(const ContainerNode &node) PJSUA2_THROW(Error) NODE_READ_STRING (crypto_node, crypto.key); NODE_READ_STRING (crypto_node, crypto.name); NODE_READ_UNSIGNED (crypto_node, crypto.flags); - this->cryptos.push_back(crypto); + this->cryptos.push_back(PJSUA2_MOVE(crypto)); } ContainerNode keying_node = this_node.readArray("keyings"); @@ -287,7 +287,7 @@ void AccountSipConfig::readObject(const ContainerNode &node) while (creds_node.hasUnread()) { AuthCredInfo cred; cred.readObject(creds_node); - authCreds.push_back(cred); + authCreds.push_back(PJSUA2_MOVE(cred)); } } @@ -762,7 +762,7 @@ void AccountConfig::fromPj(const pjsua_acc_config &prm, SipHeader new_hdr; new_hdr.fromPj(hdr); - regConfig.headers.push_back(new_hdr); + regConfig.headers.push_back(PJSUA2_MOVE(new_hdr)); hdr = hdr->next; } @@ -782,7 +782,7 @@ void AccountConfig::fromPj(const pjsua_acc_config &prm, cred.akaOp = pj2Str(src.ext.aka.op); cred.akaAmf = pj2Str(src.ext.aka.amf); - sipConfig.authCreds.push_back(cred); + sipConfig.authCreds.push_back(PJSUA2_MOVE(cred)); } sipConfig.proxies.clear(); for (i=0; inext; } presConfig.publishEnabled = PJ2BOOL(prm.publish_enabled); diff --git a/pjsip/src/pjsua2/endpoint.cpp b/pjsip/src/pjsua2/endpoint.cpp index 559e7aa345..cdfbb0cf73 100644 --- a/pjsip/src/pjsua2/endpoint.cpp +++ b/pjsip/src/pjsua2/endpoint.cpp @@ -130,7 +130,7 @@ void SslCertInfo::fromPj(const pj_ssl_cert_info &info) SslCertName cname; cname.type = info.subj_alt_name.entry[i].type; cname.name = pj2Str(info.subj_alt_name.entry[i].name); - subjectAltName.push_back(cname); + subjectAltName.push_back(PJSUA2_MOVE(cname)); } } @@ -1843,7 +1843,7 @@ void Endpoint::on_create_media_transport_srtp(pjsua_call_id call_id, crypto.key = pj2Str(srtp_opt->crypto[i].key); crypto.name = pj2Str(srtp_opt->crypto[i].name); crypto.flags = srtp_opt->crypto[i].flags; - prm.cryptos.push_back(crypto); + prm.cryptos.push_back(PJSUA2_MOVE(crypto)); } call->onCreateMediaTransportSrtp(prm); @@ -2480,7 +2480,7 @@ CodecInfoVector2 Endpoint::codecEnum2() const PJSUA2_THROW(Error) for (unsigned i = 0; idigits[i].freq1; d.freq2 = pdm->digits[i].freq2; - tdm.push_back(d); + tdm.push_back(PJSUA2_MOVE(d)); } return tdm; @@ -796,7 +796,7 @@ void AudioDevInfo::fromPj(const pjmedia_aud_dev_info &dev_info) MediaFormatAudio format; format.fromPj(dev_info.ext_fmt[i]); if (format.type == PJMEDIA_TYPE_AUDIO) - extFmt.push_back(format); + extFmt.push_back(PJSUA2_MOVE(format)); } } @@ -946,7 +946,7 @@ AudioDevInfoVector2 AudDevManager::enumDev2() const PJSUA2_THROW(Error) for (unsigned i = 0; isockOpts.push_back(so); + this->sockOpts.push_back(PJSUA2_MOVE(so)); } } @@ -375,7 +375,7 @@ void SockOptParams::readObject(const ContainerNode &node) PJSUA2_THROW(Error) int optVal = so_node.readInt("optVal"); so.setOptValInt(optVal); } - sockOpts.push_back(so); + sockOpts.push_back(PJSUA2_MOVE(so)); } } @@ -609,7 +609,7 @@ void SipMultipartPart::fromPj(const pjsip_multipart_part &prm) while (pj_hdr != &prm.hdr) { SipHeader sh; sh.fromPj(pj_hdr); - headers.push_back(sh); + headers.push_back(PJSUA2_MOVE(sh)); pj_hdr = pj_hdr->next; } @@ -757,7 +757,7 @@ void SipTxOption::fromPj(const pjsua_msg_data &prm) PJSUA2_THROW(Error) while (pj_hdr != &prm.hdr_list) { SipHeader sh; sh.fromPj(pj_hdr); - headers.push_back(sh); + headers.push_back(PJSUA2_MOVE(sh)); pj_hdr = pj_hdr->next; } @@ -770,7 +770,7 @@ void SipTxOption::fromPj(const pjsua_msg_data &prm) PJSUA2_THROW(Error) while (pj_mp != &prm.multipart_parts) { SipMultipartPart smp; smp.fromPj(*pj_mp); - multipartParts.push_back(smp); + multipartParts.push_back(PJSUA2_MOVE(smp)); pj_mp = pj_mp->next; } } From fd4cb3bc958060d45d45bba3f2b2da28de817d03 Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 15 Jan 2025 13:31:26 +0800 Subject: [PATCH 161/491] Fixed Coverity warnings (#4250) --- pjsip-apps/src/samples/pjsua2_hello_reg.cpp | 2 +- pjsip/include/pjsip/sip_parser.h | 2 +- pjsip/src/pjsip/sip_parser.c | 4 ++-- pjsip/src/pjsua-lib/pjsua_media.c | 4 +++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pjsip-apps/src/samples/pjsua2_hello_reg.cpp b/pjsip-apps/src/samples/pjsua2_hello_reg.cpp index 7660cc543e..3a47f4a0c6 100644 --- a/pjsip-apps/src/samples/pjsua2_hello_reg.cpp +++ b/pjsip-apps/src/samples/pjsua2_hello_reg.cpp @@ -42,7 +42,7 @@ int main() acfg.idUri = "sip:test@sip.pjsip.org"; acfg.regConfig.registrarUri = "sip:sip.pjsip.org"; AuthCredInfo cred("digest", "*", "test", 0, "secret"); - acfg.sipConfig.authCreds.push_back( cred ); + acfg.sipConfig.authCreds.push_back( PJSUA2_MOVE(cred) ); // Create the account MyAccount *acc = new MyAccount; diff --git a/pjsip/include/pjsip/sip_parser.h b/pjsip/include/pjsip/sip_parser.h index e1c6a5694b..8f355d7447 100644 --- a/pjsip/include/pjsip/sip_parser.h +++ b/pjsip/include/pjsip/sip_parser.h @@ -51,7 +51,7 @@ PJ_BEGIN_DECL #define PJSIP_MIN_Q1000 0 /**< For limit checks */ #define PJSIP_MAX_Q1000 PJ_MAXINT32 / 1000/**< For limit checks */ #define PJSIP_MIN_EXPIRES 0 /**< For limit checks */ -#define PJSIP_MAX_EXPIRES ((pj_uint32_t)0xFFFFFFFFUL)/**< for chk */ +//#define PJSIP_MAX_EXPIRES ((pj_uint32_t)0xFFFFFFFFUL)/**< for chk */ #define PJSIP_MIN_CSEQ 0 /**< For limit checks */ #define PJSIP_MAX_CSEQ PJ_MAXINT32 /**< For limit checks */ #define PJSIP_MIN_RETRY_AFTER 0 /**< For limit checks */ diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c index 401e13f8bb..5dc10b47e0 100644 --- a/pjsip/src/pjsip/sip_parser.c +++ b/pjsip/src/pjsip/sip_parser.c @@ -1916,8 +1916,8 @@ static void int_parse_contact_param( pjsip_contact_hdr *hdr, hdr->expires = pj_strtoul(&pvalue); if (hdr->expires == PJSIP_EXPIRES_NOT_SPECIFIED) hdr->expires--; - if (hdr->expires > PJSIP_MAX_EXPIRES) - hdr->expires = PJSIP_MAX_EXPIRES; + //if (hdr->expires > PJSIP_MAX_EXPIRES) + // hdr->expires = PJSIP_MAX_EXPIRES; #if PJSIP_MIN_EXPIRES > 0 if (hdr->expires < PJSIP_MIN_EXPIRES) hdr->expires = PJSIP_MIN_EXPIRES; diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index 07cae64fbe..9d5dc560ec 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -3006,7 +3006,9 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, /* Check if request supports PJSIP_INV_REQUIRE_SIPREC. If so Get label * attribute in SDP offer and add label attribute to SDP answer */ - if (call->inv && (call->inv->options & PJSIP_INV_REQUIRE_SIPREC)) { + if (call->inv && (call->inv->options & PJSIP_INV_REQUIRE_SIPREC) && + rem_sdp) + { pjmedia_sdp_attr *label_attr; const pj_str_t STR_LABEL_ATTR = {"label", 5}; From 45b662fc600232840b86abebc780fd18f89929d1 Mon Sep 17 00:00:00 2001 From: sauwming Date: Thu, 16 Jan 2025 15:40:44 +0800 Subject: [PATCH 162/491] Modify ALSA default max devices (#4251) --- pjmedia/include/pjmedia-audiodev/config.h | 9 +++++++++ pjmedia/include/pjmedia/audiodev.h | 3 +-- pjmedia/src/pjmedia-audiodev/alsa_dev.c | 2 +- pjmedia/src/pjmedia/audiodev.c | 6 +++--- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/pjmedia/include/pjmedia-audiodev/config.h b/pjmedia/include/pjmedia-audiodev/config.h index 2c5a1d65be..240115ad3a 100644 --- a/pjmedia/include/pjmedia-audiodev/config.h +++ b/pjmedia/include/pjmedia-audiodev/config.h @@ -41,6 +41,15 @@ PJ_BEGIN_DECL * @{ */ +/** + * This setting controls the maximum number of supported audio devices. + * + * Default: 64 + */ +#ifndef PJMEDIA_AUD_DEV_MAX_DEVS +# define PJMEDIA_AUD_DEV_MAX_DEVS 64 +#endif + /** * This setting controls the buffer length of audio device name. * diff --git a/pjmedia/include/pjmedia/audiodev.h b/pjmedia/include/pjmedia/audiodev.h index 7f74bfc7b8..7aed566663 100644 --- a/pjmedia/include/pjmedia/audiodev.h +++ b/pjmedia/include/pjmedia/audiodev.h @@ -79,7 +79,6 @@ typedef pj_int32_t pjmedia_aud_dev_index; #define PJMEDIA_AUD_INVALID_DEV -3 #define PJMEDIA_AUD_MAX_DRIVERS 16 -#define PJMEDIA_AUD_MAX_DEVS 64 /** Forward declaration for pjmedia_aud_stream */ @@ -117,7 +116,7 @@ typedef struct pjmedia_aud_subsys pjmedia_aud_driver drv[PJMEDIA_AUD_MAX_DRIVERS];/* Array of drivers. */ unsigned dev_cnt; /* Total number of devices. */ - pj_uint32_t dev_list[PJMEDIA_AUD_MAX_DEVS];/* Array of dev IDs. */ + pj_uint32_t dev_list[PJMEDIA_AUD_DEV_MAX_DEVS];/* Array dev IDs.*/ } pjmedia_aud_subsys; diff --git a/pjmedia/src/pjmedia-audiodev/alsa_dev.c b/pjmedia/src/pjmedia-audiodev/alsa_dev.c index fd868bd06e..f326c13f5e 100755 --- a/pjmedia/src/pjmedia-audiodev/alsa_dev.c +++ b/pjmedia/src/pjmedia-audiodev/alsa_dev.c @@ -43,7 +43,7 @@ #define ALSASOUND_CAPTURE 2 #define MAX_SOUND_CARDS 5 #define MAX_SOUND_DEVICES_PER_CARD 5 -#define MAX_DEVICES 32 +#define MAX_DEVICES PJMEDIA_AUD_DEV_MAX_DEVS #define MAX_MIX_NAME_LEN 64 /* Set to 1 to enable tracing */ diff --git a/pjmedia/src/pjmedia/audiodev.c b/pjmedia/src/pjmedia/audiodev.c index eea7dfe0d5..24e4e48e66 100644 --- a/pjmedia/src/pjmedia/audiodev.c +++ b/pjmedia/src/pjmedia/audiodev.c @@ -103,12 +103,12 @@ PJ_DEF(pj_status_t) pjmedia_aud_driver_init(unsigned drv_idx, /* Get number of devices */ dev_cnt = f->op->get_dev_count(f); - if (dev_cnt + aud_subsys.dev_cnt > PJMEDIA_AUD_MAX_DEVS) { + if (dev_cnt + aud_subsys.dev_cnt > PJMEDIA_AUD_DEV_MAX_DEVS) { PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because" " there are too many devices", aud_subsys.dev_cnt + dev_cnt - - PJMEDIA_AUD_MAX_DEVS)); - dev_cnt = PJMEDIA_AUD_MAX_DEVS - aud_subsys.dev_cnt; + PJMEDIA_AUD_DEV_MAX_DEVS)); + dev_cnt = PJMEDIA_AUD_DEV_MAX_DEVS - aud_subsys.dev_cnt; } /* enabling this will cause pjsua-lib initialization to fail when there From 1e2009dbeb7481ef033bbb652587d1be0dd911a9 Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 17 Jan 2025 16:55:40 +0800 Subject: [PATCH 163/491] Modify pjsua default setting of using software clock in sound port to PJ_TRUE (#4255) --- pjsip/include/pjsua-lib/pjsua.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 7feb9fc821..da555d093a 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -7240,7 +7240,7 @@ PJ_DECL(pj_status_t) pjsua_im_typing(pjsua_acc_id acc_id, * pjsua_media_config.snd_use_sw_clock. */ #ifndef PJSUA_DEFAULT_SND_USE_SW_CLOCK -# define PJSUA_DEFAULT_SND_USE_SW_CLOCK PJ_FALSE +# define PJSUA_DEFAULT_SND_USE_SW_CLOCK PJ_TRUE #endif /** From 11ad8316952cb3b7a77a030bbfeb47b3e4bae529 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Fri, 17 Jan 2025 16:06:16 +0700 Subject: [PATCH 164/491] Synchronous port removal in audio conference bridge if port is newly added (#4253) --- pjmedia/src/pjmedia/conference.c | 59 ++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c index 12f16eaac9..594d17bf50 100644 --- a/pjmedia/src/pjmedia/conference.c +++ b/pjmedia/src/pjmedia/conference.c @@ -1664,6 +1664,65 @@ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf, goto on_return; } + /* If port is new, remove it synchronously */ + if (conf_port->is_new) { + pj_bool_t found = PJ_FALSE; + + /* Find & cancel the add-op. + * Also cancel all following ops involving the slot. + * Note that after removed, the slot may be reused by another port + * so if not cancelled, those following ops may be applied to the + * wrong port. + */ + ope = conf->op_queue->next; + while (ope != conf->op_queue) { + op_entry* cancel_op; + + cancel_op = NULL; + if (ope->type == OP_ADD_PORT && ope->param.add_port.port == port) + { + found = PJ_TRUE; + cancel_op = ope; + } else if (found && ope->type == OP_CONNECT_PORTS && + (ope->param.connect_ports.src == port || + ope->param.connect_ports.sink == port)) + { + cancel_op = ope; + } else if (found && ope->type == OP_DISCONNECT_PORTS && + (ope->param.disconnect_ports.src == port || + ope->param.disconnect_ports.sink == port)) + { + cancel_op = ope; + } + + ope = ope->next; + + /* Cancel op */ + if (cancel_op) { + pj_list_erase(cancel_op); + cancel_op->type = OP_UNKNOWN; + pj_list_push_back(conf->op_queue_free, cancel_op); + } + } + + /* If the add-op is not found, it may be being executed, + * do not remove it synchronously to avoid race condition. + */ + if (found) { + op_param prm; + + /* Release mutex to avoid deadlock */ + pj_mutex_unlock(conf->mutex); + + /* Remove it */ + prm.remove_port.port = port; + op_remove_port(conf, &prm); + + pj_log_pop_indent(); + return PJ_SUCCESS; + } + } + /* Queue the operation */ ope = get_free_op_entry(conf); if (ope) { From 093da33f2e0db619c47df649b03c53c3027136a0 Mon Sep 17 00:00:00 2001 From: sauwming Date: Mon, 20 Jan 2025 18:27:00 +0800 Subject: [PATCH 165/491] Update SSL backends and unit test (#4254) --- pjlib/include/pj/ssl_sock.h | 35 +++++++++++++++++++----- pjlib/src/pj/ssl_sock_apple.m | 4 +-- pjlib/src/pj/ssl_sock_gtls.c | 8 +++--- pjlib/src/pj/ssl_sock_ossl.c | 48 +++++++++++---------------------- pjlib/src/pjlib-test/ssl_sock.c | 39 +++++++++++++-------------- 5 files changed, 68 insertions(+), 66 deletions(-) diff --git a/pjlib/include/pj/ssl_sock.h b/pjlib/include/pj/ssl_sock.h index d24be89211..566cac0ae5 100644 --- a/pjlib/include/pj/ssl_sock.h +++ b/pjlib/include/pj/ssl_sock.h @@ -447,6 +447,18 @@ typedef enum pj_ssl_cipher { PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x0000006C, PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x0000006D, + PJ_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0x0000c02c, + PJ_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0x0000c030, + PJ_TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x000000a3, + PJ_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x0000009f, + PJ_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0x0000c02b, + PJ_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0x0000c02f, + + /* TLS 1.3 cipher suites */ + PJ_TLS_AES_128_GCM_SHA256 = 0x00001301, + PJ_TLS_AES_256_GCM_SHA384 = 0x00001302, + PJ_TLS_CHACHA20_POLY1305_SHA256 = 0x00001303, + /* TLS (deprecated) */ PJ_TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x00000003, PJ_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x00000006, @@ -788,27 +800,33 @@ typedef struct pj_ssl_sock_cb typedef enum pj_ssl_sock_proto { /** - * Default protocol of backend. + * Default protocol of backend. + * Typically this will be set to all supported non-deprecated protocols, + * which, currently is TLSv1.2 and TLSv1.3. */ PJ_SSL_SOCK_PROTO_DEFAULT = 0, /** - * SSLv2.0 protocol. + * SSLv2.0 protocol. + * This protocol has been deprecated. */ PJ_SSL_SOCK_PROTO_SSL2 = (1 << 0), /** - * SSLv3.0 protocol. + * SSLv3.0 protocol. + * This protocol has been deprecated. */ PJ_SSL_SOCK_PROTO_SSL3 = (1 << 1), /** - * TLSv1.0 protocol. + * TLSv1.0 protocol. + * This protocol has been deprecated. */ PJ_SSL_SOCK_PROTO_TLS1 = (1 << 2), /** * TLSv1.1 protocol. + * This protocol has been deprecated. */ PJ_SSL_SOCK_PROTO_TLS1_1 = (1 << 3), @@ -823,11 +841,14 @@ typedef enum pj_ssl_sock_proto PJ_SSL_SOCK_PROTO_TLS1_3 = (1 << 5), /** - * Certain backend implementation e.g:OpenSSL, has feature to enable all - * protocol. + * This protocol has been deprecated. */ PJ_SSL_SOCK_PROTO_SSL23 = (1 << 16) - 1, - PJ_SSL_SOCK_PROTO_ALL = PJ_SSL_SOCK_PROTO_SSL23, + + /** + * This will enable all the backend's supported protocols. + */ + PJ_SSL_SOCK_PROTO_ALL = (1 << 16) - 1, /** * DTLSv1.0 protocol. diff --git a/pjlib/src/pj/ssl_sock_apple.m b/pjlib/src/pj/ssl_sock_apple.m index 795aba6cc0..93eed7b231 100644 --- a/pjlib/src/pj/ssl_sock_apple.m +++ b/pjlib/src/pj/ssl_sock_apple.m @@ -926,9 +926,7 @@ static pj_status_t network_create_params(pj_ssl_sock_t * ssock, /* Set min and max protocol version */ if (ssock->param.proto == PJ_SSL_SOCK_PROTO_DEFAULT) { - ssock->param.proto = PJ_SSL_SOCK_PROTO_TLS1 | - PJ_SSL_SOCK_PROTO_TLS1_1 | - PJ_SSL_SOCK_PROTO_TLS1_2 | + ssock->param.proto = PJ_SSL_SOCK_PROTO_TLS1_2 | PJ_SSL_SOCK_PROTO_TLS1_3; } diff --git a/pjlib/src/pj/ssl_sock_gtls.c b/pjlib/src/pj/ssl_sock_gtls.c index a08cf131c6..ebb4022725 100644 --- a/pjlib/src/pj/ssl_sock_gtls.c +++ b/pjlib/src/pj/ssl_sock_gtls.c @@ -433,10 +433,10 @@ static pj_status_t tls_priorities_set(pj_ssl_sock_t *ssock) pj_strset(&cipher_list, buf, 0); pj_strset(&priority, priority_buf, 0); - if (ssock->param.proto == PJ_SSL_SOCK_PROTO_DEFAULT) - ssock->param.proto = PJ_SSL_SOCK_PROTO_TLS1 | - PJ_SSL_SOCK_PROTO_TLS1_1 | - PJ_SSL_SOCK_PROTO_TLS1_2; + if (ssock->param.proto == PJ_SSL_SOCK_PROTO_DEFAULT) { + ssock->param.proto = PJ_SSL_SOCK_PROTO_TLS1_2 | + PJ_SSL_SOCK_PROTO_TLS1_3; + } /* For each level, enable only the requested protocol */ pj_strcat2(&priority, "NORMAL:-VERS-ALL:"); diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c index 8524c43b79..5017d7550e 100644 --- a/pjlib/src/pj/ssl_sock_ossl.c +++ b/pjlib/src/pj/ssl_sock_ossl.c @@ -1137,37 +1137,13 @@ static pj_status_t init_ossl_ctx(pj_ssl_sock_t *ssock) int rc; pj_status_t status; - if (ssock->param.proto == PJ_SSL_SOCK_PROTO_DEFAULT) - ssock->param.proto = PJ_SSL_SOCK_PROTO_SSL23; - - /* Determine SSL method to use */ - /* Specific version methods are deprecated since 1.1.0 */ -#if (USING_LIBRESSL && LIBRESSL_VERSION_NUMBER < 0x2020100fL)\ - || OPENSSL_VERSION_NUMBER < 0x10100000L - switch (ssock->param.proto) { - case PJ_SSL_SOCK_PROTO_TLS1: - ssl_method = (SSL_METHOD*)TLSv1_method(); - break; -#ifndef OPENSSL_NO_SSL2 - case PJ_SSL_SOCK_PROTO_SSL2: - ssl_method = (SSL_METHOD*)SSLv2_method(); - break; -#endif -#ifndef OPENSSL_NO_SSL3_METHOD - case PJ_SSL_SOCK_PROTO_SSL3: - ssl_method = (SSL_METHOD*)SSLv3_method(); -#endif - break; + if (ssock->param.proto == PJ_SSL_SOCK_PROTO_DEFAULT) { + ssock->param.proto = PJ_SSL_SOCK_PROTO_TLS1_2 | + PJ_SSL_SOCK_PROTO_TLS1_3; } -#endif if (!ssl_method) { -#if (USING_LIBRESSL && LIBRESSL_VERSION_NUMBER < 0x2020100fL)\ - || OPENSSL_VERSION_NUMBER < 0x10100000L - ssl_method = (SSL_METHOD*)SSLv23_method(); -#else ssl_method = (SSL_METHOD*)TLS_method(); -#endif #ifdef SSL_OP_NO_SSLv2 /** Check if SSLv2 is enabled */ @@ -1630,8 +1606,10 @@ static pj_status_t ssl_create(pj_ssl_sock_t *ssock) set_entropy(ssock); - if (ssock->param.proto == PJ_SSL_SOCK_PROTO_DEFAULT) - ssock->param.proto = PJ_SSL_SOCK_PROTO_SSL23; + if (ssock->param.proto == PJ_SSL_SOCK_PROTO_DEFAULT) { + ssock->param.proto = PJ_SSL_SOCK_PROTO_TLS1_2 | + PJ_SSL_SOCK_PROTO_TLS1_3; + } /* Create SSL context */ if (SERVER_SUPPORT_SESSION_REUSE && ssock->is_server) { @@ -1809,7 +1787,7 @@ static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock) enum { BUF_SIZE = 8192 }; pj_str_t cipher_list; unsigned i, j; - int ret; + int ret, ret2 = 1; if (ssock->param.ciphers_num == 0) { ret = SSL_CTX_set_cipher_list(ossock->ossl_ctx, PJ_SSL_SOCK_OSSL_CIPHERS); @@ -1859,9 +1837,15 @@ static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock) /* Put NULL termination in the generated cipher list */ cipher_list.ptr[cipher_list.slen] = '\0'; - /* Finally, set chosen cipher list */ + /* Finally, set chosen cipher list. + * SSL_CTX_set_cipher_list() is for TLSv1.2 and below, while + * SSL_CTX_set_ciphersuites() is for TLSv1.3. + */ ret = SSL_CTX_set_cipher_list(ossock->ossl_ctx, buf); - if (ret < 1) { + ret2 = SSL_CTX_set_ciphersuites(ossock->ossl_ctx, buf); + if (ret < 1 && ret2 < 1) { + PJ_LOG(4, (THIS_FILE, "Failed setting cipher list %s", + cipher_list.ptr)); pj_pool_release(tmp_pool); return GET_SSL_STATUS(ssock); } diff --git a/pjlib/src/pjlib-test/ssl_sock.c b/pjlib/src/pjlib-test/ssl_sock.c index 6e371c8c43..5a95d07149 100644 --- a/pjlib/src/pjlib-test/ssl_sock.c +++ b/pjlib/src/pjlib-test/ssl_sock.c @@ -476,7 +476,7 @@ static int https_client_test(unsigned ms_timeout) 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)); + status = pj_ssl_sock_start_connect(ssock, pool, &local_addr, &rem_addr, pj_sockaddr_get_len(&local_addr)); if (status == PJ_SUCCESS) { ssl_on_connect_complete(ssock, PJ_SUCCESS); } else if (status == PJ_EPENDING) { @@ -1611,9 +1611,8 @@ int ssl_sock_test(void) PJ_LOG(3,("", "..https client test")); ret = https_client_test(30000); - // Ignore test result as internet connection may not be available. - //if (ret != 0) - //return ret; + if (ret != 0) + return ret; #ifndef PJ_SYMBIAN @@ -1631,22 +1630,20 @@ int ssl_sock_test(void) */ #if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL) - PJ_LOG(3,("", "..echo test w/ TLSv1 and PJ_TLS_RSA_WITH_AES_256_CBC_SHA cipher")); - ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1, PJ_SSL_SOCK_PROTO_TLS1, - PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA, + PJ_LOG(3,("", "..echo test w/ TLSv1.2 and PJ_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 cipher")); + ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1_2, PJ_SSL_SOCK_PROTO_TLS1_2, + PJ_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + PJ_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, PJ_FALSE, PJ_FALSE); if (ret != 0) return ret; - /* SSLv23 is deprecated */ - /* - PJ_LOG(3,("", "..echo test w/ SSLv23 and PJ_TLS_RSA_WITH_AES_256_CBC_SHA cipher")); - ret = echo_test(PJ_SSL_SOCK_PROTO_SSL23, PJ_SSL_SOCK_PROTO_SSL23, - PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA, + PJ_LOG(3,("", "..echo test w/ TLSv1.3 and PJ_TLS_AES_128_GCM_SHA256 cipher")); + ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1_3, PJ_SSL_SOCK_PROTO_TLS1_3, + PJ_TLS_AES_128_GCM_SHA256, PJ_TLS_AES_128_GCM_SHA256, PJ_FALSE, PJ_FALSE); if (ret != 0) return ret; - */ #endif PJ_LOG(3,("", "..echo test w/ compatible proto: server TLSv1.2 vs client TLSv1.2")); @@ -1664,9 +1661,10 @@ int ssl_sock_test(void) if (ret != 0) return ret; - PJ_LOG(3,("", "..echo test w/ incompatible proto: server TLSv1 vs client SSL3")); - ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1, PJ_SSL_SOCK_PROTO_SSL3, - PJ_TLS_RSA_WITH_DES_CBC_SHA, PJ_TLS_RSA_WITH_DES_CBC_SHA, + PJ_LOG(3,("", "..echo test w/ incompatible proto: server TLSv1.3 vs client TLSv1.2")); + ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1_3, PJ_SSL_SOCK_PROTO_TLS1_2, + PJ_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + PJ_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, PJ_FALSE, PJ_FALSE); if (ret == 0) return PJ_EBUG; @@ -1682,8 +1680,9 @@ int ssl_sock_test(void) return PJ_EBUG; #endif -/* We can't seem to enable certain ciphers only. SSLSetEnabledCiphers() is - * deprecated and we only have sec_protocol_options_append_tls_ciphersuite(), +/* With Apple SSL, we can't seem to enable certain ciphers only. + * SSLSetEnabledCiphers() is deprecated and we only have + * sec_protocol_options_append_tls_ciphersuite(), * but there's no API to remove certain or all ciphers. */ #if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_APPLE && PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL) @@ -1698,14 +1697,14 @@ int ssl_sock_test(void) #if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL) PJ_LOG(3,("", "..echo test w/ client cert required but not provided")); ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT, - PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA, + -1, -1, PJ_TRUE, PJ_FALSE); if (ret == 0) return PJ_EBUG; PJ_LOG(3,("", "..echo test w/ client cert required and provided")); ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT, - PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA, + -1, -1, PJ_TRUE, PJ_TRUE); if (ret != 0) return ret; From 52b32a274766a19072cb252e9c70a97bd825ba95 Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 22 Jan 2025 09:25:31 +0800 Subject: [PATCH 166/491] Fixed timer remove node safeguard (#4259) --- pjlib/src/pj/timer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pjlib/src/pj/timer.c b/pjlib/src/pj/timer.c index 86766ddfb4..e5459aa815 100644 --- a/pjlib/src/pj/timer.c +++ b/pjlib/src/pj/timer.c @@ -330,8 +330,9 @@ static pj_timer_entry_dup * remove_node( pj_timer_heap_t *ht, size_t slot) #if ASSERT_IF_ENTRY_DESTROYED pj_assert(removed_node->dup._timer_id==removed_node->entry->_timer_id); #endif + } else { + GET_ENTRY(removed_node)->_timer_id = -1; } - GET_ENTRY(removed_node)->_timer_id = -1; GET_FIELD(removed_node, _timer_id) = -1; #if !PJ_TIMER_USE_LINKED_LIST From 52915ed21a5c38a82aa97aff6d7eefd359f7741b Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 22 Jan 2025 09:58:02 +0800 Subject: [PATCH 167/491] Modify failed pjlib test to exclusive (#4258) --- pjlib/src/pjlib-test/test.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c index ef24678f34..6e358be725 100644 --- a/pjlib/src/pjlib-test/test.c +++ b/pjlib/src/pjlib-test/test.c @@ -242,13 +242,13 @@ static int features_tests(int argc, char *argv[]) UT_ADD_TEST(&test_app.ut_app, hash_test, 0); #endif - /* Windows GH CI oftent fails with: + /* GH CI oftent fails with: 07:27:13.217 ...testing frequency accuracy (pls wait) 07:27:23.440 ....error: timestamp drifted by 3800 usec after 10020 msec */ #if INCLUDE_TIMESTAMP_TEST -# if defined(PJ_WIN32) && PJ_WIN32!=0 +# if 1 //defined(PJ_WIN32) && PJ_WIN32!=0 UT_ADD_TEST(&test_app.ut_app, timestamp_test, PJ_TEST_EXCLUSIVE | PJ_TEST_KEEP_LAST); # else @@ -264,12 +264,12 @@ static int features_tests(int argc, char *argv[]) UT_ADD_TEST(&test_app.ut_app, timer_test, 0); #endif - /* Very often sleep test failed on GitHub Windows CI, with + /* Very often sleep test failed on GitHub CI, with the thread sleeping for much longer than tolerated. So as a workaround, set it as exclusive. */ #if INCLUDE_SLEEP_TEST -# if defined(PJ_WIN32) && PJ_WIN32!=0 +# if 1 //defined(PJ_WIN32) && PJ_WIN32!=0 UT_ADD_TEST(&test_app.ut_app, sleep_test, PJ_TEST_EXCLUSIVE | PJ_TEST_KEEP_LAST); # else From 0899186444ac5d0cff9a5198cb1d4c754a45aacf Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Wed, 22 Jan 2025 12:22:12 +0700 Subject: [PATCH 168/491] Select I/O queue backend using macro PJ_IOQUEUE_IMP (#4260) --- aconfigure | 20 +++++++------ aconfigure.ac | 14 ++++----- pjlib/build/os-auto.mak.in | 1 + pjlib/build/pjlib.vcproj | 12 -------- pjlib/build/pjlib.vcxproj | 27 +----------------- pjlib/include/pj/compat/os_auto.h.in | 6 ++-- pjlib/include/pj/config.h | 39 +++++++++++++++++++++++++- pjlib/src/pj/ioqueue_dummy.c | 7 +++++ pjlib/src/pj/ioqueue_epoll.c | 7 +++++ pjlib/src/pj/ioqueue_kqueue.c | 5 ++++ pjlib/src/pj/ioqueue_select.c | 7 +++++ pjlib/src/pj/ioqueue_symbian.cpp | 7 +++++ pjlib/src/pj/ioqueue_uwp.cpp | 6 ++++ pjlib/src/pj/ioqueue_winnt.c | 9 ++++++ pjlib/src/pjlib-test/ioq_perf.c | 2 +- pjlib/src/pjlib-test/ioq_stress_test.c | 12 ++++---- pjlib/src/pjlib-test/ioq_tcp.c | 2 +- pjlib/src/pjlib-test/ioq_udp.c | 2 +- 18 files changed, 117 insertions(+), 68 deletions(-) diff --git a/aconfigure b/aconfigure index b5d3446319..cf7ece6db5 100755 --- a/aconfigure +++ b/aconfigure @@ -732,7 +732,6 @@ ac_external_gsm ac_external_speex ac_no_pjsua2 ac_shared_libraries -ac_linux_poll ac_os_objs ac_std_cpp_lib ac_target_arch @@ -6839,13 +6838,9 @@ fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking ioqueue backend" >&5 printf %s "checking ioqueue backend... " >&6; } -ac_os_objs=ioqueue_select.o -ac_linux_poll=select - case $target in *darwin* | *bsd*) # Check whether --enable-kqueue was given. @@ -6853,18 +6848,23 @@ if test ${enable_kqueue+y} then : enableval=$enable_kqueue; if test "$enable_kqueue" = "yes"; then - ac_os_objs=ioqueue_kqueue.o { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: kqueue()" >&5 printf "%s\n" "kqueue()" >&6; } + printf "%s\n" "#define PJ_IOQUEUE_IMP PJ_IOQUEUE_IMP_KQUEUE" >>confdefs.h + else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: select()" >&5 printf "%s\n" "select()" >&6; } + printf "%s\n" "#define PJ_IOQUEUE_IMP PJ_IOQUEUE_IMP_SELECT" >>confdefs.h + fi else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: select()" >&5 printf "%s\n" "select()" >&6; } + printf "%s\n" "#define PJ_IOQUEUE_IMP PJ_IOQUEUE_IMP_SELECT" >>confdefs.h + ;; esac @@ -6877,21 +6877,23 @@ if test ${enable_epoll+y} then : enableval=$enable_epoll; if test "$enable_epoll" = "yes"; then - ac_os_objs=ioqueue_epoll.o { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: /dev/epoll" >&5 printf "%s\n" "/dev/epoll" >&6; } - printf "%s\n" "#define PJ_HAS_LINUX_EPOLL 1" >>confdefs.h + printf "%s\n" "#define PJ_IOQUEUE_IMP PJ_IOQUEUE_IMP_EPOLL" >>confdefs.h - ac_linux_poll=epoll else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: select()" >&5 printf "%s\n" "select()" >&6; } + printf "%s\n" "#define PJ_IOQUEUE_IMP PJ_IOQUEUE_IMP_SELECT" >>confdefs.h + fi else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: select()" >&5 printf "%s\n" "select()" >&6; } + printf "%s\n" "#define PJ_IOQUEUE_IMP PJ_IOQUEUE_IMP_SELECT" >>confdefs.h + ;; esac diff --git a/aconfigure.ac b/aconfigure.ac index 279870e9d6..7d304595e2 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -569,26 +569,24 @@ dnl ###################### dnl # ioqueue selection dnl # AC_SUBST(ac_os_objs) -AC_SUBST(ac_linux_poll) AC_MSG_CHECKING([ioqueue backend]) -ac_os_objs=ioqueue_select.o -ac_linux_poll=select - case $target in *darwin* | *bsd*) AC_ARG_ENABLE(kqueue, AS_HELP_STRING([--enable-kqueue], [Use kqueue ioqueue on macos/BSD (experimental)]), [ if test "$enable_kqueue" = "yes"; then - ac_os_objs=ioqueue_kqueue.o AC_MSG_RESULT([kqueue()]) + AC_DEFINE(PJ_IOQUEUE_IMP, PJ_IOQUEUE_IMP_KQUEUE) else AC_MSG_RESULT([select()]) + AC_DEFINE(PJ_IOQUEUE_IMP, PJ_IOQUEUE_IMP_SELECT) fi ], [ AC_MSG_RESULT([select()]) + AC_DEFINE(PJ_IOQUEUE_IMP, PJ_IOQUEUE_IMP_SELECT) ] ) ;; @@ -597,16 +595,16 @@ case $target in AS_HELP_STRING([--enable-epoll], [Use /dev/epoll ioqueue on Linux (experimental)]), [ if test "$enable_epoll" = "yes"; then - ac_os_objs=ioqueue_epoll.o AC_MSG_RESULT([/dev/epoll]) - AC_DEFINE(PJ_HAS_LINUX_EPOLL,1) - ac_linux_poll=epoll + AC_DEFINE(PJ_IOQUEUE_IMP, PJ_IOQUEUE_IMP_EPOLL) else AC_MSG_RESULT([select()]) + AC_DEFINE(PJ_IOQUEUE_IMP, PJ_IOQUEUE_IMP_SELECT) fi ], [ AC_MSG_RESULT([select()]) + AC_DEFINE(PJ_IOQUEUE_IMP, PJ_IOQUEUE_IMP_SELECT) ] ) ;; diff --git a/pjlib/build/os-auto.mak.in b/pjlib/build/os-auto.mak.in index 4d3724c3e7..11890d31e3 100644 --- a/pjlib/build/os-auto.mak.in +++ b/pjlib/build/os-auto.mak.in @@ -10,6 +10,7 @@ AC_OS_OBJS=@ac_os_objs@ # export PJLIB_OBJS += $(AC_OS_OBJS) \ addr_resolv_sock.o \ + ioqueue_dummy.o ioqueue_epoll.o ioqueue_kqueue.o ioqueue_select.o \ log_writer_stdout.o \ os_timestamp_common.o \ pool_policy_malloc.o sock_bsd.o sock_select.o diff --git a/pjlib/build/pjlib.vcproj b/pjlib/build/pjlib.vcproj index bc4e2b2c75..96eb22b541 100644 --- a/pjlib/build/pjlib.vcproj +++ b/pjlib/build/pjlib.vcproj @@ -5405,7 +5405,6 @@ > true - - true - true - true - true - true - true - true - true - true - true - true - true - true - true - true - true - true - true - true - true - true - true - true - true - + true true diff --git a/pjlib/include/pj/compat/os_auto.h.in b/pjlib/include/pj/compat/os_auto.h.in index b8ff5769a2..217a8b6e6e 100644 --- a/pjlib/include/pj/compat/os_auto.h.in +++ b/pjlib/include/pj/compat/os_auto.h.in @@ -122,6 +122,9 @@ typedef int socklen_t; #endif +/* Select I/O queue backend. */ +#undef PJ_IOQUEUE_IMP + /** * If this macro is set, it tells select I/O Queue that select() needs to * be given correct value of nfds (i.e. largest fd + 1). This requires @@ -134,9 +137,6 @@ */ #undef PJ_SELECT_NEEDS_NFDS -/* Was Linux epoll support enabled */ -#undef PJ_HAS_LINUX_EPOLL - /* Is errno a good way to retrieve OS errors? */ #undef PJ_HAS_ERRNO_VAR diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h index 49ff05ba32..f4809ce788 100644 --- a/pjlib/include/pj/config.h +++ b/pjlib/include/pj/config.h @@ -690,6 +690,43 @@ # define PJ_ACTIVESOCK_MAX_CONSECUTIVE_ACCEPT_ERROR 50 #endif + +/* + * I/O queue implementation backends. + * Select one of these implementations in PJ_IOQUEUE_IMP. + */ + +/** No/dummy I/O queue */ +#define PJ_IOQUEUE_IMP_NONE 0 + +/** Using select() */ +#define PJ_IOQUEUE_IMP_SELECT 1 + +/** Using epoll() (experimental) */ +#define PJ_IOQUEUE_IMP_EPOLL 2 + +/** Using Windows I/O Completion Ports (experimental) */ +#define PJ_IOQUEUE_IMP_IOCP 3 + +/** Using MacOS/BSD kqueue (experimental) */ +#define PJ_IOQUEUE_IMP_KQUEUE 4 + +/** Using Windows UWP socket (deprecated) */ +#define PJ_IOQUEUE_IMP_UWP 5 + +/** Using Symbian (deprecated) */ +#define PJ_IOQUEUE_IMP_SYMBIAN 6 + +/** + * I/O queue implementation backend. + * + * Default: PJ_IOQUEUE_IMP_SELECT + */ +#ifndef PJ_IOQUEUE_IMP +# define PJ_IOQUEUE_IMP PJ_IOQUEUE_IMP_SELECT +#endif + + /** * Constants for declaring the maximum handles that can be supported by * a single IOQ framework. This constant might not be relevant to the @@ -1068,7 +1105,7 @@ /** Using OpenSSL */ #define PJ_SSL_SOCK_IMP_OPENSSL 1 -/**< Using GnuTLS */ +/** Using GnuTLS */ #define PJ_SSL_SOCK_IMP_GNUTLS 2 /** Using Apple's Secure Transport (deprecated in MacOS 10.15 & iOS 13.0) */ diff --git a/pjlib/src/pj/ioqueue_dummy.c b/pjlib/src/pj/ioqueue_dummy.c index 5e7c2a3519..cf3b1f1f11 100644 --- a/pjlib/src/pj/ioqueue_dummy.c +++ b/pjlib/src/pj/ioqueue_dummy.c @@ -26,6 +26,11 @@ #include #include + +/* Only build when the backend is using dummy/none. */ +#if PJ_IOQUEUE_IMP == PJ_IOQUEUE_IMP_NONE + + #define THIS_FILE "ioqueue" #define PJ_IOQUEUE_IS_READ_OP(op) \ @@ -196,3 +201,5 @@ PJ_DEF(pj_oshandle_t) pj_ioqueue_get_os_handle( pj_ioqueue_t *ioqueue ) PJ_UNUSED_ARG(ioqueue); return NULL; } + +#endif /* PJ_IOQUEUE_IMP == PJ_IOQUEUE_IMP_NONE */ diff --git a/pjlib/src/pj/ioqueue_epoll.c b/pjlib/src/pj/ioqueue_epoll.c index 2845a23eaf..4f81391c1b 100644 --- a/pjlib/src/pj/ioqueue_epoll.c +++ b/pjlib/src/pj/ioqueue_epoll.c @@ -36,6 +36,11 @@ #include #include + +/* Only build when the backend is using epoll. */ +#if PJ_IOQUEUE_IMP == PJ_IOQUEUE_IMP_EPOLL + + #include #include #include @@ -1082,3 +1087,5 @@ PJ_DEF(pj_oshandle_t) pj_ioqueue_get_os_handle( pj_ioqueue_t *ioqueue ) { return ioqueue ? (pj_oshandle_t)&ioqueue->epfd : NULL; } + +#endif /* PJ_IOQUEUE_IMP == PJ_IOQUEUE_IMP_EPOLL */ diff --git a/pjlib/src/pj/ioqueue_kqueue.c b/pjlib/src/pj/ioqueue_kqueue.c index bd319f70b5..e3937e4a0a 100644 --- a/pjlib/src/pj/ioqueue_kqueue.c +++ b/pjlib/src/pj/ioqueue_kqueue.c @@ -32,6 +32,9 @@ #include #include +/* Only build when the backend is using kqueue. */ +#if PJ_IOQUEUE_IMP == PJ_IOQUEUE_IMP_KQUEUE + #include #define os_kqueue_open kqueue @@ -729,3 +732,5 @@ PJ_DEF(pj_oshandle_t) pj_ioqueue_get_os_handle( pj_ioqueue_t *ioqueue ) { return ioqueue ? (pj_oshandle_t)&ioqueue->kfd : NULL; } + +#endif /* PJ_IOQUEUE_IMP == PJ_IOQUEUE_IMP_KQUEUE */ diff --git a/pjlib/src/pj/ioqueue_select.c b/pjlib/src/pj/ioqueue_select.c index 5641cf8cbb..9409616108 100644 --- a/pjlib/src/pj/ioqueue_select.c +++ b/pjlib/src/pj/ioqueue_select.c @@ -40,6 +40,11 @@ #include #include + +/* Only build when the backend is using select(). */ +#if PJ_IOQUEUE_IMP == PJ_IOQUEUE_IMP_SELECT + + /* Now that we have access to OS'es , lets check again that * PJ_IOQUEUE_MAX_HANDLES is not greater than FD_SETSIZE */ @@ -1142,3 +1147,5 @@ PJ_DEF(pj_oshandle_t) pj_ioqueue_get_os_handle( pj_ioqueue_t *ioqueue ) PJ_UNUSED_ARG(ioqueue); return NULL; } + +#endif /* PJ_IOQUEUE_IMP == PJ_IOQUEUE_IMP_SELECT */ diff --git a/pjlib/src/pj/ioqueue_symbian.cpp b/pjlib/src/pj/ioqueue_symbian.cpp index 9673f0b2f3..82c749c2e6 100644 --- a/pjlib/src/pj/ioqueue_symbian.cpp +++ b/pjlib/src/pj/ioqueue_symbian.cpp @@ -24,6 +24,11 @@ #include #include + +/* Only build when the backend is using Symbian. */ +#if PJ_IOQUEUE_IMP == PJ_IOQUEUE_IMP_SYMBIAN + + #include "os_symbian.h" class CIoqueueCallback; @@ -869,3 +874,5 @@ PJ_DEF(pj_oshandle_t) pj_ioqueue_get_os_handle( pj_ioqueue_t *ioqueue ) PJ_UNUSED_ARG(ioqueue); return NULL; } + +#endif /* PJ_IOQUEUE_IMP == PJ_IOQUEUE_IMP_SYMBIAN */ diff --git a/pjlib/src/pj/ioqueue_uwp.cpp b/pjlib/src/pj/ioqueue_uwp.cpp index a261ef0850..66da7ef896 100644 --- a/pjlib/src/pj/ioqueue_uwp.cpp +++ b/pjlib/src/pj/ioqueue_uwp.cpp @@ -22,6 +22,10 @@ #include #include +/* Only build when the backend is using Windows UWP socket. */ +#if PJ_IOQUEUE_IMP == PJ_IOQUEUE_IMP_UWP + + #include #include @@ -367,3 +371,5 @@ PJ_DEF(pj_oshandle_t) pj_ioqueue_get_os_handle( pj_ioqueue_t *ioqueue ) PJ_UNUSED_ARG(ioqueue); return NULL; } + +#endif /* PJ_IOQUEUE_IMP == PJ_IOQUEUE_IMP_UWP */ diff --git a/pjlib/src/pj/ioqueue_winnt.c b/pjlib/src/pj/ioqueue_winnt.c index 19e568cbc5..bbaf87322d 100644 --- a/pjlib/src/pj/ioqueue_winnt.c +++ b/pjlib/src/pj/ioqueue_winnt.c @@ -29,6 +29,12 @@ #include +/* Only build when the backend is Windows I/O Completion Ports. */ +#if PJ_IOQUEUE_IMP == PJ_IOQUEUE_IMP_IOCP + + +#define THIS_FILE "ioq_winnt" + #if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 # include #elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 @@ -1513,3 +1519,6 @@ PJ_DEF(pj_oshandle_t) pj_ioqueue_get_os_handle( pj_ioqueue_t *ioqueue ) { return ioqueue ? (pj_oshandle_t)ioqueue->iocp : NULL; } + + +#endif /* PJ_IOQUEUE_IMP == PJ_IOQUEUE_IMP_IOCP */ diff --git a/pjlib/src/pjlib-test/ioq_perf.c b/pjlib/src/pjlib-test/ioq_perf.c index a4405abbba..9383028f92 100644 --- a/pjlib/src/pjlib-test/ioq_perf.c +++ b/pjlib/src/pjlib-test/ioq_perf.c @@ -565,7 +565,7 @@ static int ioqueue_perf_test_imp(const pj_ioqueue_cfg *cfg) } static pj_ioqueue_epoll_flag epoll_flags[] = { -#if PJ_HAS_LINUX_EPOLL +#if PJ_IOQUEUE_IMP==PJ_IOQUEUE_IMP_EPOLL PJ_IOQUEUE_EPOLL_EXCLUSIVE, PJ_IOQUEUE_EPOLL_ONESHOT, 0, diff --git a/pjlib/src/pjlib-test/ioq_stress_test.c b/pjlib/src/pjlib-test/ioq_stress_test.c index ed27ef1217..61f22fc4e2 100644 --- a/pjlib/src/pjlib-test/ioq_stress_test.c +++ b/pjlib/src/pjlib-test/ioq_stress_test.c @@ -769,7 +769,7 @@ static test_desc tests[128] = { .cfg.n_clients = 1, .cfg.repeat = 4 }, - #if PJ_HAS_LINUX_EPOLL + #if PJ_IOQUEUE_IMP==PJ_IOQUEUE_IMP_EPOLL { .cfg.title = "basic udp (single thread, EPOLLEXCLUSIVE)", .cfg.max_fd = 4, @@ -831,7 +831,7 @@ static test_desc tests[128] = { .cfg.n_clients = 1, .cfg.repeat = 4 }, - #if PJ_HAS_LINUX_EPOLL + #if PJ_IOQUEUE_IMP==PJ_IOQUEUE_IMP_EPOLL { .cfg.title = "basic tcp (single thread, EPOLLEXCLUSIVE)", .cfg.max_fd = 6, @@ -893,7 +893,7 @@ static test_desc tests[128] = { .cfg.n_clients = 1, .cfg.repeat = 2 }, - #if PJ_HAS_LINUX_EPOLL + #if PJ_IOQUEUE_IMP==PJ_IOQUEUE_IMP_EPOLL { .cfg.title = "failed tcp connect (EPOLLEXCLUSIVE)", .cfg.expected_ret_code = RETCODE_CONNECT_FAILED, @@ -980,7 +980,7 @@ static test_desc tests[128] = { .cfg.n_clients = MAX_ASYNC, .cfg.repeat = 4 }, - #if PJ_HAS_LINUX_EPOLL + #if PJ_IOQUEUE_IMP==PJ_IOQUEUE_IMP_EPOLL /* EPOLLEXCLUSIVE (udp). */ { @@ -1051,7 +1051,7 @@ static test_desc tests[128] = { .cfg.n_clients = MAX_ASYNC, .cfg.repeat = 4 }, - #if PJ_HAS_LINUX_EPOLL + #if PJ_IOQUEUE_IMP==PJ_IOQUEUE_IMP_EPOLL { .cfg.title = "tcp (multithreads, EPOLLEXCLUSIVE)", .cfg.max_fd = 6, @@ -1121,7 +1121,7 @@ static test_desc tests[128] = { .cfg.n_clients = MAX_ASYNC, .cfg.repeat = 4 }, - #if PJ_HAS_LINUX_EPOLL + #if PJ_IOQUEUE_IMP==PJ_IOQUEUE_IMP_EPOLL { .cfg.title = "tcp (multithreads, sequenced, concur=0, EPOLLEXCLUSIVE)", .cfg.max_fd = 6, diff --git a/pjlib/src/pjlib-test/ioq_tcp.c b/pjlib/src/pjlib-test/ioq_tcp.c index 3f665b4cc2..06b56a9518 100644 --- a/pjlib/src/pjlib-test/ioq_tcp.c +++ b/pjlib/src/pjlib-test/ioq_tcp.c @@ -946,7 +946,7 @@ int tcp_ioqueue_test() { pj_ioqueue_epoll_flag epoll_flags[] = { PJ_IOQUEUE_EPOLL_AUTO, -#if PJ_HAS_LINUX_EPOLL +#if PJ_IOQUEUE_IMP==PJ_IOQUEUE_IMP_EPOLL PJ_IOQUEUE_EPOLL_EXCLUSIVE, PJ_IOQUEUE_EPOLL_ONESHOT, 0 diff --git a/pjlib/src/pjlib-test/ioq_udp.c b/pjlib/src/pjlib-test/ioq_udp.c index 6c696dd1dc..cb038ffe42 100644 --- a/pjlib/src/pjlib-test/ioq_udp.c +++ b/pjlib/src/pjlib-test/ioq_udp.c @@ -1241,7 +1241,7 @@ static int udp_ioqueue_test_imp(const pj_ioqueue_cfg *cfg) int udp_ioqueue_test() { pj_ioqueue_epoll_flag epoll_flags[] = { -#if PJ_HAS_LINUX_EPOLL +#if PJ_IOQUEUE_IMP==PJ_IOQUEUE_IMP_EPOLL PJ_IOQUEUE_EPOLL_AUTO, PJ_IOQUEUE_EPOLL_EXCLUSIVE, PJ_IOQUEUE_EPOLL_ONESHOT, From cdb129406a126335fc1008e386aef7b4a3342013 Mon Sep 17 00:00:00 2001 From: sauwming Date: Thu, 23 Jan 2025 11:01:27 +0800 Subject: [PATCH 169/491] Various fixes for Apple SSL backend (#4257) --- pjlib/src/pj/ssl_sock_apple.m | 31 ++++++++++++++++--------------- pjlib/src/pjlib-test/ssl_sock.c | 11 +++++++++-- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/pjlib/src/pj/ssl_sock_apple.m b/pjlib/src/pj/ssl_sock_apple.m index 93eed7b231..7570ccdcb2 100644 --- a/pjlib/src/pj/ssl_sock_apple.m +++ b/pjlib/src/pj/ssl_sock_apple.m @@ -297,7 +297,7 @@ pj_status_t ssl_network_event_poll() if (ssock->is_closing || !ssock->pool || (!ssock->is_server && !assock->connection) || - (ssock->is_server && !assock->listener)) + (ssock->is_server && !assock->listener && !assock->connection)) { PJ_LOG(3, (THIS_FILE, "Warning: Discarding SSL event type %d of " "a closing socket %p", event->type, ssock)); @@ -804,10 +804,8 @@ static pj_status_t network_start_read(pj_ssl_sock_t *ssock, if (is_complete && (context == NULL || nw_content_context_get_is_final(context))) { - return; - } - - if (error != NULL) { + status = PJ_EEOF; + } else if (error != NULL) { errno = nw_error_get_error_code(error); if (errno == 89) { /* Since error 89 is network intentionally cancelled by @@ -823,7 +821,7 @@ static pj_status_t network_start_read(pj_ssl_sock_t *ssock, dispatch_block_t schedule_next_receive = ^{ /* If there was no error in receiving, request more data. */ - if (!error && !is_complete && assock->connection) { + if (!error && !is_complete && status != PJ_EEOF) { network_start_read(ssock, async_count, buff_size, readbuf, flags); } @@ -1000,9 +998,11 @@ static pj_status_t network_create_params(pj_ssl_sock_t * ssock, */ sec_protocol_options_set_tls_resumption_enabled(sec_options, false); - /* SSL verification options */ - sec_protocol_options_set_peer_authentication_required(sec_options, - true); + /* SSL peer authentication options */ + if (ssock->is_server && ssock->param.require_client_cert) { + sec_protocol_options_set_peer_authentication_required(sec_options, + true); + } /* Handshake flow: * 1. Server's challenge block, provide server's trust @@ -1164,10 +1164,8 @@ static pj_status_t network_setup_connection(pj_ssl_sock_t *ssock, errno = nw_error_get_error_code(error); warn("Connection failed %p", assock); status = PJ_STATUS_FROM_OS(errno); -#if SSL_DEBUG - PJ_LOG(3, (THIS_FILE, "SSL state and errno %d %d", state, errno)); -#endif - call_cb = PJ_TRUE; + if (ssock->ssl_state == SSL_STATE_HANDSHAKING) + call_cb = PJ_TRUE; } if (state == nw_connection_state_ready) { @@ -1398,7 +1396,8 @@ static pj_status_t network_start_connect(pj_ssl_sock_t *ssock, assock = PJ_POOL_ZALLOC_T(pool, applessl_sock_t); - assock->queue = dispatch_queue_create("ssl_queue", DISPATCH_QUEUE_SERIAL); + assock->queue = dispatch_queue_create("ssl_queue", + DISPATCH_QUEUE_CONCURRENT); assock->ev_semaphore = dispatch_semaphore_create(0); if (!assock->queue || !assock->ev_semaphore) { ssl_destroy(&assock->base); @@ -1430,7 +1429,6 @@ static void close_connection(applessl_sock_t *assock) assock->connection = nil; nw_connection_force_cancel(conn); - nw_release(conn); /* We need to wait until the connection is at cancelled state, * otherwise events will still be delivered even though we @@ -1448,6 +1446,9 @@ static void close_connection(applessl_sock_t *assock) "%p %d", assock, assock->con_state)); } + nw_connection_set_state_changed_handler(conn, nil); + nw_release(conn); + #if SSL_DEBUG PJ_LOG(3, (THIS_FILE, "SSL connection %p closed", assock)); #endif diff --git a/pjlib/src/pjlib-test/ssl_sock.c b/pjlib/src/pjlib-test/ssl_sock.c index 5a95d07149..2ca31e9664 100644 --- a/pjlib/src/pjlib-test/ssl_sock.c +++ b/pjlib/src/pjlib-test/ssl_sock.c @@ -466,7 +466,6 @@ static int https_client_test(unsigned ms_timeout) 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); @@ -658,6 +657,12 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto, pj_str_t privkey_file = pj_str(CERT_PRIVKEY_FILE); pj_str_t privkey_pass = pj_str(CERT_PRIVKEY_PASS); +#if (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_APPLE) + /* We store private key in Keychain. */ + privkey_file = pj_str(""); + privkey_pass = pj_str(""); +#endif + #if (defined(TEST_LOAD_FROM_FILES) && TEST_LOAD_FROM_FILES==1) status = pj_ssl_cert_load_from_files(pool, &ca_file, &cert_file, &privkey_file, &privkey_pass, @@ -810,8 +815,10 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto, /* Clean up sockets */ { - pj_time_val delay = {0, 100}; + /* The delay must be at least PJ_SSL_SOCK_DELAYED_CLOSE_TIMEOUT. */ + pj_time_val delay = {0, 500}; while (pj_ioqueue_poll(ioqueue, &delay) > 0); + pj_timer_heap_poll(timer, &delay); } if (state_serv.err || state_cli.err) { From de3f2e1e64fca8ef48bda3774caeae6cabd306f0 Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 24 Jan 2025 10:20:28 +0800 Subject: [PATCH 170/491] Set CI vars in GH workflow file (#4263) --- .github/workflows/ci-linux.yml | 4 ++-- .github/workflows/ci-mac.yml | 4 ++-- .github/workflows/ci-win.yml | 33 +++++++++++++++++++++++---------- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 7a5293a7fa..0fa25ef527 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -6,8 +6,8 @@ on: pull_request: types: [opened, synchronize, reopened] env: - CI_ARGS: ${{ vars.CI_UBUNTU_ARGS }} - CI_MODE: ${{ vars.CI_MODE }} + CI_ARGS: -w 3 --shuffle --stdout-buf 1 + CI_MODE: --ci-mode MAKE_FAST: make -j 3 jobs: default-build: diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index a550773842..793c6fbc77 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -6,8 +6,8 @@ on: pull_request: types: [opened, synchronize, reopened] env: - CI_ARGS: ${{ vars.CI_MAC_ARGS }} - CI_MODE: ${{ vars.CI_MODE }} + CI_ARGS: -w 2 --shuffle --stdout-buf 1 + CI_MODE: --ci-mode MAKE_FAST: make -j 2 jobs: default-build: diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index 6a76d386bc..40556634ae 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -5,6 +5,9 @@ on: - 'master' pull_request: types: [opened, synchronize, reopened] +env: + CI_WIN_ARGS: -w 3 --shuffle + CI_MODE: --ci-mode jobs: default: runs-on: windows-latest @@ -82,21 +85,24 @@ jobs: $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" cd pjlib/bin - ./pjlib-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} ${{ vars.CI_MODE }} + $args = $env:CI_WIN_ARGS -split ' ' + ./pjlib-test-i386-Win32-vc14-Release.exe $args $env:CI_MODE shell: powershell - name: pjlib-util-test run: | $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" cd pjlib-util/bin - ./pjlib-util-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} + $args = $env:CI_WIN_ARGS -split ' ' + ./pjlib-util-test-i386-Win32-vc14-Release.exe $args shell: powershell - name: pjmedia-test run: | $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" cd pjmedia/bin - ./pjmedia-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} + $args = $env:CI_WIN_ARGS -split ' ' + ./pjmedia-test-i386-Win32-vc14-Release.exe $args shell: powershell openssl-2: @@ -161,7 +167,8 @@ jobs: $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" cd pjsip/bin - ./pjsip-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} + $args = $env:CI_WIN_ARGS -split ' ' + ./pjsip-test-i386-Win32-vc14-Release.exe $args shell: powershell - name: python pjsua tests run: | @@ -214,7 +221,8 @@ jobs: $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" cd pjnath/bin - ./pjnath-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} + $args = $env:CI_WIN_ARGS -split ' ' + ./pjnath-test-i386-Win32-vc14-Release.exe $args shell: powershell gnu-tls: @@ -333,28 +341,32 @@ jobs: $env:SDL_DIR = Get-Content .\sdl_dir.txt $env:PATH+=";$env:SDL_DIR\lib\x86;" cd pjlib/bin - ./pjlib-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} ${{ vars.CI_MODE }} + $args = $env:CI_WIN_ARGS -split ' ' + ./pjlib-test-i386-Win32-vc14-Release.exe $args $env:CI_MODE shell: powershell - name: pjlib-util-test run: | $env:SDL_DIR = Get-Content .\sdl_dir.txt $env:PATH+=";$env:SDL_DIR\lib\x86;" cd pjlib-util/bin - ./pjlib-util-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} + $args = $env:CI_WIN_ARGS -split ' ' + ./pjlib-util-test-i386-Win32-vc14-Release.exe $args shell: powershell - name: pjmedia-test run: | $env:SDL_DIR = Get-Content .\sdl_dir.txt $env:PATH+=";$env:SDL_DIR\lib\x86;" cd pjmedia/bin - ./pjmedia-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} + $args = $env:CI_WIN_ARGS -split ' ' + ./pjmedia-test-i386-Win32-vc14-Release.exe $args shell: powershell - name: pjnath-test run: | $env:SDL_DIR = Get-Content .\sdl_dir.txt $env:PATH+=";$env:SDL_DIR\lib\x86;" cd pjnath/bin - ./pjnath-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} ${{ vars.CI_MODE }} + $args = $env:CI_WIN_ARGS -split ' ' + ./pjnath-test-i386-Win32-vc14-Release.exe $args $env:CI_MODE shell: powershell vid-libvpx-schannel-2: @@ -517,7 +529,8 @@ jobs: $env:SDL_DIR = Get-Content .\sdl_dir.txt $env:PATH+=";$env:SDL_DIR\lib\x86;" cd pjsip/bin - ./pjsip-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} + $args = $env:CI_WIN_ARGS -split ' ' + ./pjsip-test-i386-Win32-vc14-Release.exe $args shell: powershell build-win-vid-ffmpeg: From 377a80cefe6647bccce3915840b4b238ba523be3 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Fri, 24 Jan 2025 13:48:31 +0700 Subject: [PATCH 171/491] Fix various compile errors & warnings in MSVC2005 (#4268) --- .../src/pjlib-util-test/resolver_test.c | 2 +- pjlib-util/src/pjlib-util-test/test.c | 6 +++-- pjlib/src/pj/ioqueue_winnt.c | 2 ++ pjlib/src/pjlib-test/fifobuf.c | 2 +- pjlib/src/pjlib-test/test.c | 16 +++++++------ pjlib/src/pjlib-test/test_util.h | 5 ++-- pjnath/src/pjnath-test/ice_test.c | 6 +++-- pjsip-apps/src/samples/pcaputil.c | 2 +- pjsip/src/pjsip-ua/sip_siprec.c | 24 +++++++++---------- pjsip/src/pjsip/sip_util.c | 2 +- pjsip/src/test/transport_test.c | 3 ++- pjsip/src/test/transport_udp_test.c | 2 +- pjsip/src/test/tsx_uac_test.c | 2 +- 13 files changed, 42 insertions(+), 32 deletions(-) diff --git a/pjlib-util/src/pjlib-util-test/resolver_test.c b/pjlib-util/src/pjlib-util-test/resolver_test.c index 2d23906ea1..ff346b8b67 100644 --- a/pjlib-util/src/pjlib-util-test/resolver_test.c +++ b/pjlib-util/src/pjlib-util-test/resolver_test.c @@ -1018,7 +1018,7 @@ static int simple_test(void) NULL, return -300); pj_sem_wait(sem); - pj_thread_sleep(set.qretr_delay * 1.2); + pj_thread_sleep((unsigned)(set.qretr_delay * 1.2)); /* Both servers must get packet */ diff --git a/pjlib-util/src/pjlib-util-test/test.c b/pjlib-util/src/pjlib-util-test/test.c index cc519c949e..ee8609e9df 100644 --- a/pjlib-util/src/pjlib-util-test/test.c +++ b/pjlib-util/src/pjlib-util-test/test.c @@ -24,8 +24,10 @@ pj_pool_factory *mem; struct test_app_t test_app = { - .param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | - PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_INDENT, + {0}, /* ut_app */ + PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | + PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_INDENT, + /* param_log_decor */ }; void app_perror(const char *msg, pj_status_t rc) diff --git a/pjlib/src/pj/ioqueue_winnt.c b/pjlib/src/pj/ioqueue_winnt.c index bbaf87322d..89e0e95fb4 100644 --- a/pjlib/src/pj/ioqueue_winnt.c +++ b/pjlib/src/pj/ioqueue_winnt.c @@ -45,6 +45,8 @@ # include #endif +/* For GetAcceptExSockaddrs() on MSVC2005 */ +#pragma comment(lib, "mswsock.lib") /* The address specified in AcceptEx() must be 16 more than the size of * SOCKADDR (source: MSDN). diff --git a/pjlib/src/pjlib-test/fifobuf.c b/pjlib/src/pjlib-test/fifobuf.c index b6fd486867..a8350026fd 100644 --- a/pjlib/src/pjlib-test/fifobuf.c +++ b/pjlib/src/pjlib-test/fifobuf.c @@ -105,7 +105,7 @@ static int fifobuf_rolling_test() /* Free cunks, leave some chunks for the next repeat */ n = N/4 + (pj_rand() % (N/4)); - while (pj_list_size(&chunks) > n) { + while ((int)pj_list_size(&chunks) > n) { chunk = chunks.next; pj_list_erase(chunk); pj_fifobuf_free(&fifo, chunk); diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c index 6e358be725..ba1a9bbb3d 100644 --- a/pjlib/src/pjlib-test/test.c +++ b/pjlib/src/pjlib-test/test.c @@ -37,12 +37,14 @@ pj_pool_factory *mem; struct test_app_t test_app = { - .param_echo_sock_type = 0, - .param_echo_server = ECHO_SERVER_ADDRESS, - .param_echo_port = ECHO_SERVER_START_PORT, - .param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | - PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_INDENT, - .param_ci_mode = PJ_FALSE, + {0}, /* .ut_app */ + 0, /* .param_echo_sock_type */ + ECHO_SERVER_ADDRESS, /* .param_echo_server */ + ECHO_SERVER_START_PORT, /* .param_echo_port */ + PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | + PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_INDENT, + /* .param_log_decor */ + PJ_FALSE, /* .param_ci_mode */ }; int null_func() @@ -387,7 +389,7 @@ int test_inner(int argc, char *argv[]) } } - if (argc-1 > 0 && stat.nruns==argc-1) { + if (argc-1 > 0 && (int)stat.nruns==argc-1) { /* cmdline specifies test(s) to run, and the number of runs * matches that. That means all requested tests have been run. */ diff --git a/pjlib/src/pjlib-test/test_util.h b/pjlib/src/pjlib-test/test_util.h index 7eb4575c7e..28f5b08a8b 100644 --- a/pjlib/src/pjlib-test/test_util.h +++ b/pjlib/src/pjlib-test/test_util.h @@ -148,11 +148,12 @@ PJ_INLINE(void) ut_list_tests(ut_app_t *ut_app, const char *title) PJ_INLINE(pj_status_t) ut_run_tests(ut_app_t *ut_app, const char *title, int argc, char *argv[]) { - pj_test_runner_param runner_prm; - pj_test_runner_param_default(&runner_prm); pj_test_runner *runner; pj_test_stat stat; pj_status_t status; + pj_test_runner_param runner_prm; + + pj_test_runner_param_default(&runner_prm); if (ut_app->prm_shuffle) { PJ_LOG(3,(THIS_FILE, "Shuffling tests, random seed=%d", diff --git a/pjnath/src/pjnath-test/ice_test.c b/pjnath/src/pjnath-test/ice_test.c index 1189bfe7f0..27ec063344 100644 --- a/pjnath/src/pjnath-test/ice_test.c +++ b/pjnath/src/pjnath-test/ice_test.c @@ -1119,11 +1119,13 @@ int ice_test(void *p) rc = 0; if (test_id >= ICE_TEST_START_ARRAY) { - i = test_id - ICE_TEST_START_ARRAY; - struct sess_cfg_t *cfg = &sess_cfg[i]; + struct sess_cfg_t *cfg; unsigned delay[] = { 50, 2000 }; unsigned d; + i = test_id - ICE_TEST_START_ARRAY; + cfg = &sess_cfg[i]; + PJ_LOG(3,(THIS_FILE, " %s", cfg->title)); /* For each test item, test with various answer delay */ diff --git a/pjsip-apps/src/samples/pcaputil.c b/pjsip-apps/src/samples/pcaputil.c index f87bd43dc3..075f878e98 100644 --- a/pjsip-apps/src/samples/pcaputil.c +++ b/pjsip-apps/src/samples/pcaputil.c @@ -427,7 +427,7 @@ static void pcap2wav(const struct args *args) ts_gap = pj_ntohl(pkt1.rtp->ts) - pj_ntohl(pkt0.rtp->ts) - samples_cnt; - if (ts_gap <= param.info.clock_rate * GAP_IGNORE_SECONDS) { /* Ignore gap >30s */ + if (ts_gap <= (long)param.info.clock_rate * GAP_IGNORE_SECONDS) { /* Ignore gap >30s */ while (ts_gap >= (long)samples_per_frame) { pcm_frame.buf = pcm; pcm_frame.size = samples_per_frame * 2; diff --git a/pjsip/src/pjsip-ua/sip_siprec.c b/pjsip/src/pjsip-ua/sip_siprec.c index 96d4b69497..af33bf5955 100644 --- a/pjsip/src/pjsip-ua/sip_siprec.c +++ b/pjsip/src/pjsip-ua/sip_siprec.c @@ -114,7 +114,8 @@ PJ_DEF(pj_status_t) pjsip_siprec_init_module(pjsip_endpoint *endpt) */ PJ_DEF(pj_status_t) pjsip_siprec_verify_require_hdr(pjsip_require_hdr *req_hdr) { - for (int i=0; icount; ++i) { + unsigned i; + for (i=0; icount; ++i) { /* Check request has the siprec value in the Require header.*/ if (pj_stricmp(&req_hdr->values[i], &STR_SIPREC)==0) { @@ -140,6 +141,7 @@ PJ_DEF(pj_status_t) pjsip_siprec_verify_request(pjsip_rx_data *rdata, pj_status_t status = PJ_SUCCESS; const char *warn_text = NULL; pjsip_hdr res_hdr_list; + unsigned mi; /* Init return arguments. */ if (p_tdata) *p_tdata = NULL; @@ -180,7 +182,7 @@ PJ_DEF(pj_status_t) pjsip_siprec_verify_request(pjsip_rx_data *rdata, } /* Check that the media attribute label exist in the SDP */ - for (unsigned mi=0; mimedia_count; ++mi) { + for (mi=0; mimedia_count; ++mi) { if (!pjmedia_sdp_media_find_attr(sdp_offer->media[mi], &STR_LABEL, NULL)){ code = PJSIP_SC_BAD_REQUEST; @@ -263,22 +265,20 @@ PJ_DEF(pj_status_t) pjsip_siprec_verify_request(pjsip_rx_data *rdata, * Find siprec metadata from the message body */ PJ_DEF(pj_status_t) pjsip_siprec_get_metadata(pj_pool_t *pool, - pjsip_msg_body *body, - pj_str_t* metadata) + pjsip_msg_body *body, + pj_str_t* metadata) { - pjsip_media_type application_metadata; - - pjsip_media_type_init2(&application_metadata, - "application", "rs-metadata"); - + pjsip_media_type app_metadata; pjsip_multipart_part *metadata_part; - metadata_part = pjsip_multipart_find_part(body, - &application_metadata, NULL); + + PJ_UNUSED_ARG(pool); + pjsip_media_type_init2(&app_metadata, "application", "rs-metadata"); + metadata_part = pjsip_multipart_find_part(body, &app_metadata, NULL); if(!metadata_part) return PJ_ENOTFOUND; - metadata->ptr = (char*)metadata_part->body->data; + metadata->ptr = (char*)metadata_part->body->data; metadata->slen = metadata_part->body->len; return PJ_SUCCESS; diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c index 886228dbc5..3d5c3912c5 100644 --- a/pjsip/src/pjsip/sip_util.c +++ b/pjsip/src/pjsip/sip_util.c @@ -1030,7 +1030,7 @@ PJ_DEF(pj_status_t) pjsip_process_route_set(pjsip_tx_data *tdata, tdata->tp_sel.type == PJSIP_TPSELECTOR_LISTENER) && tdata->tp_sel.u.ptr) { - pjsip_transport_type_e tp_type; + pjsip_transport_type_e tp_type = PJSIP_TRANSPORT_UNSPECIFIED; if (tdata->tp_sel.type == PJSIP_TPSELECTOR_TRANSPORT) tp_type = tdata->tp_sel.u.transport->key.type; diff --git a/pjsip/src/test/transport_test.c b/pjsip/src/test/transport_test.c index 6f4fda98aa..c06f7f5f77 100644 --- a/pjsip/src/test/transport_test.c +++ b/pjsip/src/test/transport_test.c @@ -707,10 +707,11 @@ int transport_rt_test( pjsip_transport_type_e tp_type, /* Start threads! */ for (i=0; i Date: Fri, 24 Jan 2025 14:49:59 +0800 Subject: [PATCH 172/491] OpenSSL: Set ciphersuites only if not using BoringSSL (#4269) --- pjlib/src/pj/ssl_sock_ossl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c index 5017d7550e..ec01435bdc 100644 --- a/pjlib/src/pj/ssl_sock_ossl.c +++ b/pjlib/src/pj/ssl_sock_ossl.c @@ -1842,7 +1842,9 @@ static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock) * SSL_CTX_set_ciphersuites() is for TLSv1.3. */ ret = SSL_CTX_set_cipher_list(ossock->ossl_ctx, buf); +#if !USING_BORINGSSL ret2 = SSL_CTX_set_ciphersuites(ossock->ossl_ctx, buf); +#endif if (ret < 1 && ret2 < 1) { PJ_LOG(4, (THIS_FILE, "Failed setting cipher list %s", cipher_list.ptr)); From bab33d6686c6f23871a6243bd40f13e80445871a Mon Sep 17 00:00:00 2001 From: Noel Morgan Date: Tue, 28 Jan 2025 01:51:50 -0600 Subject: [PATCH 173/491] Added support for updated RFC7866 content-type sub type with XML extension (#4270) --- pjsip/src/pjsip-ua/sip_siprec.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pjsip/src/pjsip-ua/sip_siprec.c b/pjsip/src/pjsip-ua/sip_siprec.c index af33bf5955..2602e28700 100644 --- a/pjsip/src/pjsip-ua/sip_siprec.c +++ b/pjsip/src/pjsip-ua/sip_siprec.c @@ -275,6 +275,13 @@ PJ_DEF(pj_status_t) pjsip_siprec_get_metadata(pj_pool_t *pool, pjsip_media_type_init2(&app_metadata, "application", "rs-metadata"); metadata_part = pjsip_multipart_find_part(body, &app_metadata, NULL); + /* Fallback to XML extension rs-metadata+xml if needed per rfc*/ + if (!metadata_part) { + pjsip_media_type_init2(&app_metadata, "application", + "rs-metadata+xml"); + metadata_part = pjsip_multipart_find_part(body, &app_metadata, NULL); + } + if(!metadata_part) return PJ_ENOTFOUND; @@ -282,4 +289,4 @@ PJ_DEF(pj_status_t) pjsip_siprec_get_metadata(pj_pool_t *pool, metadata->slen = metadata_part->body->len; return PJ_SUCCESS; -} +} \ No newline at end of file From c36ed2cdb57cdbde30d1b6df3adde5bb2211d3e1 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Tue, 28 Jan 2025 16:58:55 +0700 Subject: [PATCH 174/491] Minor modifications to Android build and samples to match new documentation (#4271) * To streamline the command, also clean swig and pjsua jni output directories when make distclean and realclean is called * Kotlin sample: add account, modify video size and bandwidth, and audio codec priorities to use AMR-WB * Android CLI app: fix armeabi hardcoded arch and also copy stdc++.so --- Makefile | 2 +- pjsip-apps/src/pjsua/android/jni/Makefile | 13 ++++++-- .../pjsip/pjsua2/app_kotlin/MainActivity.kt | 33 ++++++++++++++++--- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index ce315129ab..b1f379e701 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ all clean dep depend print: done distclean realclean: - for dir in $(DIRS); do \ + for dir in $(DIRS) pjsip-apps/src/swig pjsip-apps/src/pjsua/android/jni; do \ if $(MAKE) $(MAKE_FLAGS) -C $$dir $@; then \ true; \ else \ diff --git a/pjsip-apps/src/pjsua/android/jni/Makefile b/pjsip-apps/src/pjsua/android/jni/Makefile index 6d56b19680..e1a1046899 100644 --- a/pjsip-apps/src/pjsua/android/jni/Makefile +++ b/pjsip-apps/src/pjsua/android/jni/Makefile @@ -9,7 +9,7 @@ MY_MODULES := $(MY_MODULE_PATH)/pjsua_app.o \ $(MY_MODULE_PATH)/pjsua_app_legacy.o OUT_DIR := ../build/jni -LIBPJSUA_SO := ../app/src/main/jniLibs/armeabi/libpjsua.so +LIBPJSUA_SO := ../app/src/main/jniLibs/$(TARGET_ARCH)/libpjsua.so # Env settings, e.g: path to SWIG, JDK, java(.exe), javac(.exe) MY_SWIG := swig @@ -23,10 +23,12 @@ MY_LDFLAGS := $(PJ_LDXXFLAGS) $(PJ_LDXXLIBS) $(MY_JNI_LDFLAGS) $(LDFLAGS) MY_PACKAGE_NAME := org.pjsip.pjsua MY_PACKAGE_PATH := ../app/src/main/java/$(subst .,/,$(MY_PACKAGE_NAME)) -all: $(LIBPJSUA_SO) java +MY_STD_CPP := ../app/src/main/jniLibs/$(TARGET_ARCH)/libc++_shared.so + +all: $(LIBPJSUA_SO) java $(MY_STD_CPP) $(LIBPJSUA_SO): $(OUT_DIR)/pjsua_wrap.o - mkdir -p ../app/src/main/jniLibs/armeabi + mkdir -p ../app/src/main/jniLibs/$(TARGET_ARCH) $(PJ_CXX) -shared -o $(LIBPJSUA_SO) \ $(OUT_DIR)/pjsua_wrap.o $(OUT_DIR)/pjsua_app_callback.o \ $(MY_MODULES) \ @@ -54,3 +56,8 @@ ifneq (,$(findstring PJMEDIA_VIDEO_DEV_HAS_ANDROID=1,$(ANDROID_CFLAGS))) @echo "Copying Android camera helper components..." cp $(PJDIR)/pjmedia/src/pjmedia-videodev/android/PjCamera*.java $(MY_PACKAGE_PATH)/.. endif + +$(MY_STD_CPP): $(STD_CPP_LIB) + cp -v $< $@ + + diff --git a/pjsip-apps/src/swig/java/android/app-kotlin/src/main/java/org/pjsip/pjsua2/app_kotlin/MainActivity.kt b/pjsip-apps/src/swig/java/android/app-kotlin/src/main/java/org/pjsip/pjsua2/app_kotlin/MainActivity.kt index a89f52e557..a48e485c6b 100644 --- a/pjsip-apps/src/swig/java/android/app-kotlin/src/main/java/org/pjsip/pjsua2/app_kotlin/MainActivity.kt +++ b/pjsip-apps/src/swig/java/android/app-kotlin/src/main/java/org/pjsip/pjsua2/app_kotlin/MainActivity.kt @@ -20,10 +20,15 @@ import java.lang.ref.WeakReference /* Configs */ // Account ID -const val ACC_ID_URI = "sip:localhost" +const val ACC_DOMAIN = "pjsip.org" +const val ACC_USER = "101" +const val ACC_PASSWD = "" +const val ACC_ID_URI = "Kotlin " +const val ACC_REGISTRAR = "sip:sip.pjsip.org;transport=tls" +const val ACC_PROXY = "sip:sip.pjsip.org;lr;transport=tls" // Peer to call -const val CALL_DST_URI = "sip:192.168.1.9:6000" +const val CALL_DST_URI = "MicroSIP " // Camera ID used for video call. // Use VidDevManager::enumDev2() to get available cameras & IDs. @@ -273,8 +278,18 @@ class MainActivity : AppCompatActivity(), android.os.Handler.Callback { g.ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_UDP, sipTpConfig) + g.ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_TLS, + TransportConfig()) + val accCfg = AccountConfig() accCfg.idUri = ACC_ID_URI + accCfg.regConfig.registrarUri = ACC_REGISTRAR + accCfg.sipConfig.authCreds.add( + AuthCredInfo("Digest", "*", ACC_USER, 0, + ACC_PASSWD) + ) + accCfg.sipConfig.proxies.add( ACC_PROXY ) + accCfg.videoConfig.autoShowIncoming = true accCfg.videoConfig.autoTransmitOutgoing = true accCfg.videoConfig.defaultCaptureDevice = VIDEO_CAPTURE_DEVICE_ID @@ -291,6 +306,14 @@ class MainActivity : AppCompatActivity(), android.os.Handler.Callback { } findViewById(R.id.text_info).text = "Library started" + /* Prioritize AMR-WB */ + try { + g.ep.codecSetPriority("AMR-WB", 255) + g.ep.codecSetPriority("AMR/8000", 254) + } catch (e: Exception) { + println(e) + } + /* Fix camera orientation to portrait mode (for front camera) */ try { g.ep.vidDevManager().setCaptureOrient(VIDEO_CAPTURE_DEVICE_ID, @@ -306,8 +329,10 @@ class MainActivity : AppCompatActivity(), android.os.Handler.Callback { } } var vcp = g.ep.getVideoCodecParam(codecId) - vcp.encFmt.width = 240 - vcp.encFmt.height = 320 + vcp.encFmt.width = 480 + vcp.encFmt.height = 640 + vcp.encFmt.avgBps = 1024000 + vcp.encFmt.maxBps = 5000000 g.ep.setVideoCodecParam(codecId, vcp) } catch (e: Exception) { println(e) From 960597e69b68709da61e71ddd627b6f51850ae18 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Wed, 29 Jan 2025 13:36:34 +0700 Subject: [PATCH 175/491] Various works on SWIG Java (#4273) * Various works on SWIG Java 1. Fix type mapping (SWIGTYPE_*): a. Map C "void*" & "void**" to Java long (was SWIGTYPE_p_void & SWIGTYPE_p_p_void which are not really usable), this should fix #4242. b. Map pjmedia_aud_dev_index to int. c. Map unsigned char[20] for SslCertInfo.serialNo to Java "short array" This also updates pjsua.i, e.g: tab->space, reorder things. 2. Update swig_java_pjsua2.vcxproj: a. Rename config "Debug" & "Release" to "Debug-Dynamic" & "Release-Dynamic" in , as the project actually builds dynamic libs. Also fix the property sheet dependencies from *-static to *-dynamic. b. Update other settings, e.g: built tool version from 140 to 143. 3. Update symbols.lst: added missing new types, tab->space, reorder alphabetically. * Update ci-win.yml * Add sample code for passing user data using utilTimerSchedule() * Add sample for cancelling timer --- .github/workflows/ci-win.yml | 2 +- pjproject-vs14.sln | 60 +-- pjsip-apps/build/swig_java_pjsua2.vcxproj | 117 +++-- .../org/pjsip/pjsua2/app/MainActivity.java | 2 + .../main/java/org/pjsip/pjsua2/app/MyApp.java | 11 +- pjsip-apps/src/swig/java/sample.java | 32 +- pjsip-apps/src/swig/java/sample2.java | 5 + pjsip-apps/src/swig/pjsua2.i | 181 ++++---- pjsip-apps/src/swig/symbols.i | 400 ++++++++++-------- pjsip-apps/src/swig/symbols.lst | 54 +-- 10 files changed, 468 insertions(+), 396 deletions(-) diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index 40556634ae..efc40dcbc1 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -41,7 +41,7 @@ jobs: call "%PROGRAMFILES%\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" set INCLUDE=%INCLUDE%;%JAVA_HOME%\include;%JAVA_HOME%\include\win32 cd pjsip-apps/build - msbuild swig_java_pjsua2.vcxproj /p:PlatformToolset=v143 /p:Configuration=Debug /p:Platform=win32 /p:UseEnv=true + msbuild swig_java_pjsua2.vcxproj /p:PlatformToolset=v143 /p:Configuration=Debug-Dynamic /p:Platform=win32 /p:UseEnv=true shell: cmd openssl-1: diff --git a/pjproject-vs14.sln b/pjproject-vs14.sln index 7ec360db4c..57f6588476 100644 --- a/pjproject-vs14.sln +++ b/pjproject-vs14.sln @@ -2382,36 +2382,36 @@ Global {5BCF2773-3825-4D91-9D72-3E2F650DF1DB}.Release-Static|Win32.Build.0 = Release-Static|Win32 {5BCF2773-3825-4D91-9D72-3E2F650DF1DB}.Release-Static|x64.ActiveCfg = Release-Static|x64 {5BCF2773-3825-4D91-9D72-3E2F650DF1DB}.Release-Static|x64.Build.0 = Release-Static|x64 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug|ARM.ActiveCfg = Debug|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug|Win32.ActiveCfg = Debug|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug|x64.ActiveCfg = Debug|x64 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Dynamic|Any CPU.ActiveCfg = Debug|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Dynamic|ARM.ActiveCfg = Debug|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Dynamic|ARM64.ActiveCfg = Debug|ARM64 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Dynamic|Win32.ActiveCfg = Debug|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Dynamic|x64.ActiveCfg = Debug|x64 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Static|Any CPU.ActiveCfg = Debug|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Static|ARM.ActiveCfg = Debug|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Static|ARM64.ActiveCfg = Debug|ARM64 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Static|Win32.ActiveCfg = Debug|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Static|x64.ActiveCfg = Debug|x64 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release|Any CPU.ActiveCfg = Release|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release|ARM.ActiveCfg = Release|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release|ARM64.ActiveCfg = Release|ARM64 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release|Win32.ActiveCfg = Release|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release|x64.ActiveCfg = Release|x64 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Dynamic|Any CPU.ActiveCfg = Release|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Dynamic|ARM.ActiveCfg = Release|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Dynamic|ARM64.ActiveCfg = Release|ARM64 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Dynamic|Win32.ActiveCfg = Release|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Dynamic|x64.ActiveCfg = Release|x64 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Static|Any CPU.ActiveCfg = Release|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Static|ARM.ActiveCfg = Release|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Static|ARM64.ActiveCfg = Release|ARM64 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Static|Win32.ActiveCfg = Release|Win32 - {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Static|x64.ActiveCfg = Release|x64 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug|Any CPU.ActiveCfg = Debug-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug|ARM.ActiveCfg = Debug-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug|ARM64.ActiveCfg = Debug-Dynamic|ARM64 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug|Win32.ActiveCfg = Debug-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug|x64.ActiveCfg = Debug-Dynamic|x64 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Dynamic|Any CPU.ActiveCfg = Debug-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Dynamic|ARM.ActiveCfg = Debug-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Dynamic|ARM64.ActiveCfg = Debug-Dynamic|ARM64 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Dynamic|Win32.ActiveCfg = Debug-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Dynamic|x64.ActiveCfg = Debug-Dynamic|x64 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Static|Any CPU.ActiveCfg = Debug-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Static|ARM.ActiveCfg = Debug-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Static|ARM64.ActiveCfg = Debug-Dynamic|ARM64 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Static|Win32.ActiveCfg = Debug-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Debug-Static|x64.ActiveCfg = Debug-Dynamic|x64 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release|Any CPU.ActiveCfg = Release-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release|ARM.ActiveCfg = Release-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release|ARM64.ActiveCfg = Release-Dynamic|ARM64 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release|Win32.ActiveCfg = Release-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release|x64.ActiveCfg = Release-Dynamic|x64 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Dynamic|Any CPU.ActiveCfg = Release-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Dynamic|ARM.ActiveCfg = Release-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Dynamic|ARM64.ActiveCfg = Release-Dynamic|ARM64 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Dynamic|Win32.ActiveCfg = Release-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Dynamic|x64.ActiveCfg = Release-Dynamic|x64 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Static|Any CPU.ActiveCfg = Release-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Static|ARM.ActiveCfg = Release-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Static|ARM64.ActiveCfg = Release-Dynamic|ARM64 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Static|Win32.ActiveCfg = Release-Dynamic|Win32 + {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C}.Release-Static|x64.ActiveCfg = Release-Dynamic|x64 {D738A812-CE37-40A4-B2FA-D81488C12C87}.Debug|Any CPU.ActiveCfg = Debug|Win32 {D738A812-CE37-40A4-B2FA-D81488C12C87}.Debug|ARM.ActiveCfg = Debug|ARM {D738A812-CE37-40A4-B2FA-D81488C12C87}.Debug|ARM64.ActiveCfg = Debug|ARM64 diff --git a/pjsip-apps/build/swig_java_pjsua2.vcxproj b/pjsip-apps/build/swig_java_pjsua2.vcxproj index 16b9e1feba..a30422a001 100644 --- a/pjsip-apps/build/swig_java_pjsua2.vcxproj +++ b/pjsip-apps/build/swig_java_pjsua2.vcxproj @@ -1,28 +1,28 @@  - - Debug + + Debug-Dynamic ARM64 - - Debug + + Debug-Dynamic Win32 - - Debug + + Debug-Dynamic x64 - - Release + + Release-Dynamic ARM64 - - Release + + Release-Dynamic Win32 - - Release + + Release-Dynamic x64 @@ -32,116 +32,114 @@ {63AC6D7A-5D97-40C2-9AF2-E13AA1F9567C} pjsua2 swig_java_pjsua2 + 10.0.26100.0 - + DynamicLibrary - v140 MultiByte true + v143 - + DynamicLibrary - v140 MultiByte + v143 - + DynamicLibrary - v140 MultiByte true + v143 - + DynamicLibrary - v140 MultiByte true - + DynamicLibrary - v140 MultiByte + v143 - + DynamicLibrary - v140 MultiByte - + - + - + - + - + - + - + - + - + <_ProjectFileVersion>14.0.25431.1 ..\src\swig\java\output\ - + MinimumRecommendedRules.ruleset - + MinimumRecommendedRules.ruleset - + MinimumRecommendedRules.ruleset - + MinimumRecommendedRules.ruleset - + MinimumRecommendedRules.ruleset - + MinimumRecommendedRules.ruleset - + Disabled ../../pjsip/include;../../pjlib/include;../../pjlib-util/include;../../pjmedia/include;../../pjnath/include;%(AdditionalIncludeDirectories) true EnableFastChecks - MultiThreadedDebug Level3 EditAndContinue @@ -153,7 +151,7 @@ MachineX86 - + X64 @@ -162,7 +160,6 @@ ../../pjsip/include;../../pjlib/include;../../pjlib-util/include;../../pjmedia/include;../../pjnath/include;%(AdditionalIncludeDirectories) true EnableFastChecks - MultiThreadedDebug Level3 ProgramDatabase @@ -174,7 +171,7 @@ MachineX64 - + Disabled @@ -192,10 +189,9 @@ true - + ../../pjsip/include;../../pjlib/include;../../pjlib-util/include;../../pjmedia/include;../../pjnath/include;%(AdditionalIncludeDirectories) - MultiThreaded Level3 ProgramDatabase @@ -209,13 +205,12 @@ MachineX86 - + X64 ../../pjsip/include;../../pjlib/include;../../pjlib-util/include;../../pjmedia/include;../../pjnath/include;%(AdditionalIncludeDirectories) - MultiThreaded Level3 ProgramDatabase @@ -229,7 +224,7 @@ MachineX64 - + ../../pjsip/include;../../pjlib/include;../../pjlib-util/include;../../pjmedia/include;../../pjnath/include;%(AdditionalIncludeDirectories) @@ -254,7 +249,7 @@ - %40echo off + %40echo off %40echo ************************************************************** %40echo - Make sure %27swig%27 and %27javac%27 locations are in your PATH environment vars. %40echo - Make sure Java SDK header file directories @@ -278,8 +273,8 @@ javac -d $(OutDir) -classpath $(OutDir) $(OutDir)..\sample.java %40echo ************************************************************** %40echo off - $(OutDir)pjsua2_wrap.cpp;$(OutDir)pjsua2_wrap.h;%(Outputs) - %40echo off + $(OutDir)pjsua2_wrap.cpp;$(OutDir)pjsua2_wrap.h;%(Outputs) + %40echo off %40echo ************************************************************** %40echo - Make sure %27swig%27 and %27javac%27 locations are in your PATH environment vars. %40echo - Make sure Java SDK header file directories @@ -303,7 +298,7 @@ javac -d $(OutDir) -classpath $(OutDir) $(OutDir)..\sample.java %40echo ************************************************************** %40echo off - %40echo off + %40echo off %40echo ************************************************************** %40echo - Make sure %27swig%27 and %27javac%27 locations are in your PATH environment vars. %40echo - Make sure Java SDK header file directories @@ -327,9 +322,9 @@ javac -d $(OutDir) -classpath $(OutDir) $(OutDir)..\sample.java %40echo ************************************************************** %40echo off - %(Filename)_wrap.cxx;%(Outputs) - %(Filename)_wrap.cxx;%(Outputs) - %40echo off + %(Filename)_wrap.cxx;%(Outputs) + %(Filename)_wrap.cxx;%(Outputs) + %40echo off %40echo ************************************************************** %40echo - Make sure %27swig%27 and %27javac%27 locations are in your PATH environment vars. %40echo - Make sure Java SDK header file directories @@ -353,8 +348,8 @@ javac -d $(OutDir) -classpath $(OutDir) $(OutDir)..\sample.java %40echo ************************************************************** %40echo off - $(OutDir)pjsua2_wrap.cpp;$(OutDir)pjsua2_wrap.h;%(Outputs) - %40echo off + $(OutDir)pjsua2_wrap.cpp;$(OutDir)pjsua2_wrap.h;%(Outputs) + %40echo off %40echo ************************************************************** %40echo - Make sure %27swig%27 and %27javac%27 locations are in your PATH environment vars. %40echo - Make sure Java SDK header file directories @@ -378,7 +373,7 @@ javac -d $(OutDir) -classpath $(OutDir) $(OutDir)..\sample.java %40echo ************************************************************** %40echo off - %40echo off + %40echo off %40echo ************************************************************** %40echo - Make sure %27swig%27 and %27javac%27 locations are in your PATH environment vars. %40echo - Make sure Java SDK header file directories @@ -402,8 +397,8 @@ javac -d $(OutDir) -classpath $(OutDir) $(OutDir)..\sample.java %40echo ************************************************************** %40echo off - $(OutDir)pjsua2_wrap.cpp;$(OutDir)pjsua2_wrap.h;%(Outputs) - $(OutDir)pjsua2_wrap.cpp;$(OutDir)pjsua2_wrap.h;%(Outputs) + $(OutDir)pjsua2_wrap.cpp;$(OutDir)pjsua2_wrap.h;%(Outputs) + $(OutDir)pjsua2_wrap.cpp;$(OutDir)pjsua2_wrap.h;%(Outputs) diff --git a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java index 39888cd60f..ca9519c7dc 100644 --- a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java +++ b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java @@ -670,6 +670,8 @@ public void notifyCallMediaEvent(MyCall call, OnCallMediaEventParam prm) } } + public void notifyTimer(OnTimerParam prm) {} + /* === end of MyAppObserver ==== */ } diff --git a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java index adb994d7b9..eb754c1565 100644 --- a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java +++ b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java @@ -34,6 +34,7 @@ interface MyAppObserver abstract void notifyBuddyState(MyBuddy buddy); abstract void notifyChangeNetwork(); abstract void notifyCallMediaEvent(MyCall call, OnCallMediaEventParam prm); + abstract void notifyTimer(OnTimerParam prm); } @@ -314,9 +315,17 @@ public void writeObject(ContainerNode node) } } +class MyEndpoint extends Endpoint +{ + @Override + public void onTimer(OnTimerParam prm) + { + MyApp.observer.notifyTimer(prm); + } +} class MyApp extends pjsua2 { - public static Endpoint ep = new Endpoint(); + public static MyEndpoint ep = new MyEndpoint(); public static MyAppObserver observer; public ArrayList accList = new ArrayList(); diff --git a/pjsip-apps/src/swig/java/sample.java b/pjsip-apps/src/swig/java/sample.java index a50c97ef3b..f8e92658cb 100644 --- a/pjsip-apps/src/swig/java/sample.java +++ b/pjsip-apps/src/swig/java/sample.java @@ -25,6 +25,11 @@ class MyObserver implements MyAppObserver { private static MyCall currentCall = null; private boolean del_call_scheduled = false; + private MyApp app = null; + + public MyObserver(MyApp app_) { + app = app_; + } public void check_call_deletion() { @@ -84,6 +89,11 @@ public void notifyChangeNetwork() {} @Override public void notifyCallMediaEvent(MyCall call, OnCallMediaEventParam prm) {} + + @Override + public void notifyTimer(OnTimerParam prm) { + app.ep.utilLogWrite(3, "-TIMER-", "OnTimer invoked: user data=" + prm.getUserData()); + } } class MyShutdownHook extends Thread { @@ -103,7 +113,7 @@ public void run() { public class sample { private static MyApp app = new MyApp(); - private static MyObserver observer = new MyObserver(); + private static MyObserver observer = new MyObserver(app); private static MyAccount account = null; private static AccountConfig accCfg = null; private static MyCall call = null; @@ -124,6 +134,16 @@ private void setOutputVidWin() {} private static void runWorker() { try { app.init(observer, ".", true); + + // Schedule a timer + long token = app.ep.utilTimerSchedule(1000, -100); + // Immediately cancel it, callback should not be invoked + app.ep.utilTimerCancel(token); + + // Schedule another timer + app.ep.utilLogWrite(3, "-TIMER-", "Scheduling timer: timeout=1000ms, user data=-99"); + app.ep.utilTimerSchedule(1000, -99); + } catch (Exception e) { System.out.println(e); app.deinit(); @@ -145,7 +165,7 @@ private static void runWorker() { "passwd")); StringVector proxy = sipCfg.getProxies(); - proxy.add("sip:pjsip.org;transport=tcp"); + proxy.add("sip:sip.pjsip.org;transport=tcp"); AccountRegConfig regCfg = accCfg.getRegConfig(); regCfg.setRegistrarUri("sip:pjsip.org"); @@ -171,15 +191,9 @@ private static void runWorker() { while (!Thread.currentThread().isInterrupted()) { // Handle events MyApp.ep.libHandleEvents(10); - + // Check if any call instance need to be deleted observer.check_call_deletion(); - - try { - Thread.currentThread().sleep(50); - } catch (InterruptedException ie) { - break; - } } app.deinit(); } diff --git a/pjsip-apps/src/swig/java/sample2.java b/pjsip-apps/src/swig/java/sample2.java index 30c1f50346..4cfeabfb3d 100644 --- a/pjsip-apps/src/swig/java/sample2.java +++ b/pjsip-apps/src/swig/java/sample2.java @@ -98,6 +98,11 @@ public void notifyBuddyState(MyBuddy buddy) {} @Override public void notifyChangeNetwork() {} + + @Override + public void notifyTimer(OnTimerParam prm) { + System.out.println("OnTimer invoked, user data=" + prm.getUserData()); + } } class MyThread extends Thread { diff --git a/pjsip-apps/src/swig/pjsua2.i b/pjsip-apps/src/swig/pjsua2.i index 5c7de25a79..d9b9c11803 100644 --- a/pjsip-apps/src/swig/pjsua2.i +++ b/pjsip-apps/src/swig/pjsua2.i @@ -3,7 +3,7 @@ // // Suppress few warnings // -#pragma SWIG nowarn=312 // 312: nested struct (in types.h, sip_auth.h) +#pragma SWIG nowarn=312 // 312: nested struct (in types.h, sip_auth.h) // // Header section @@ -14,6 +14,22 @@ using namespace std; using namespace pj; %} +// +// STL stuff. +// +%include "std_string.i" +%include "std_vector.i" +%include "std_map.i" + +// Android string handling +%include "and_string.i" + +%template(StringVector) std::vector; +%template(IntVector) std::vector; +%template(ByteVector) std::vector; +%template(StringToStringMap) std::map; + + #ifdef SWIGPYTHON %feature("director:except") { if( $error != NULL ) { @@ -29,7 +45,7 @@ using namespace pj; #ifdef SWIGCSHARP %typemap(throws, canthrow=1) pj::Error { SWIG_CSharpSetPendingException(SWIG_CSharpApplicationException, - (std::string("C++ pj::Error:\n") + $1.info(true).c_str()).c_str()); + (std::string("C++ pj::Error:\n") + $1.info(true).c_str()).c_str()); return $null; } @@ -51,61 +67,69 @@ using namespace pj; %} #endif -// Allow C++ exceptions to be handled in Java #ifdef SWIGJAVA + // Allow C++ exceptions to be handled in Java %typemap(throws, throws="java.lang.Exception") pj::Error { - jclass excep = jenv->FindClass("java/lang/Exception"); - if (excep) - jenv->ThrowNew(excep, $1.info(true).c_str()); - return $null; -} + jclass excep = jenv->FindClass("java/lang/Exception"); + if (excep) + jenv->ThrowNew(excep, $1.info(true).c_str()); + return $null; + } // Force the Error Java class to extend java.lang.Exception %typemap(javabase) pj::Error "java.lang.Exception"; %typemap(javacode) pj::Error %{ + // Override getMessage() + public String getMessage() { + return getTitle(); + } - // Override getMessage() - public String getMessage() { - return getTitle(); - } - - // Disable serialization (check ticket #1868) - private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { - throw new java.io.NotSerializableException("Check ticket #1868!"); - } - private void readObject(java.io.ObjectInputStream in) throws java.io.IOException { - throw new java.io.NotSerializableException("Check ticket #1868!"); - } - -%} -#endif + // Disable serialization (check ticket #1868) + private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { + throw new java.io.NotSerializableException("Check ticket #1868!"); + } + private void readObject(java.io.ObjectInputStream in) throws java.io.IOException { + throw new java.io.NotSerializableException("Check ticket #1868!"); + } + %} + // map "Token"/"void *" as "long" + %apply long long { void* }; -// Constants from PJSIP libraries + // map "void **" as "long" + %apply long long { void** }; -#ifdef SWIGJAVA -%include "enumtypeunsafe.swg" -%javaconst(1); - -%pragma(java) jniclasscode=%{ - static { - try { - System.loadLibrary("openh264"); - } catch (UnsatisfiedLinkError e) { - System.err.println("Failed to load native library openh264\n" + e); - System.out.println("This could be safely ignored if you " + - "don't use OpenH264 video codec."); - } - try { - System.loadLibrary("pjsua2"); - } catch (UnsatisfiedLinkError e) { - System.err.println("Failed to load native library pjsua2\n" + e); + // map "pjmedia_aud_dev_index" as "int" + %apply int { pjmedia_aud_dev_index }; + + // map "unsigned char[20]" as "short array" for SslCertInfo.serialNo + %include "arrays_java.i" + %apply unsigned char[ANY] { unsigned char[20] }; + + // Constants from PJSIP libraries + + %include "enumtypeunsafe.swg" + %javaconst(1); + + %pragma(java) jniclasscode=%{ + static { + try { + System.loadLibrary("openh264"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Failed to load native library openh264\n" + e); + System.out.println("This could be safely ignored if you " + + "don't use OpenH264 video codec."); + } + try { + System.loadLibrary("pjsua2"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Failed to load native library pjsua2\n" + e); + } } - } -%} + %} +#endif /* SWIGJAVA */ -#endif %include "symbols.i" @@ -121,6 +145,7 @@ using namespace pj; %feature("director") FindBuddyMatch; %feature("director") AudioMediaPlayer; %feature("director") AudioMediaPort; + // PendingJob is only used on Python #ifdef SWIGPYTHON %feature("director") PendingJob; @@ -129,20 +154,6 @@ using namespace pj; %ignore pj::Endpoint::utilAddPendingJob; #endif -// -// STL stuff. -// -%include "std_string.i" -%include "std_vector.i" -%include "std_map.i" - -// Android string handling -%include "and_string.i" - -%template(StringVector) std::vector; -%template(IntVector) std::vector; -%template(ByteVector) std::vector; -%template(StringToStringMap) std::map; // // Ignore stuffs in pjsua2 @@ -166,26 +177,26 @@ using namespace pj; %include "pjsua2/siptypes.hpp" -%template(SockOptVector) std::vector; -%template(SipHeaderVector) std::vector; -%template(AuthCredInfoVector) std::vector; -%template(SrtpCryptoVector) std::vector; -%template(SipMultipartPartVector) std::vector; -%template(BuddyVector) std::vector; -%template(BuddyVector2) std::vector; -%template(AudioMediaVector) std::vector; -%template(AudioMediaVector2) std::vector; -%template(VideoMediaVector) std::vector; -%template(ToneDescVector) std::vector; -%template(ToneDigitVector) std::vector; -%template(ToneDigitMapVector) std::vector; -%template(AudioDevInfoVector) std::vector; -%template(AudioDevInfoVector2) std::vector; -%template(CodecInfoVector) std::vector; -%template(CodecInfoVector2) std::vector; -%template(VideoDevInfoVector) std::vector; -%template(VideoDevInfoVector2) std::vector; -%template(CodecFmtpVector) std::vector; +%template(SockOptVector) std::vector; +%template(SipHeaderVector) std::vector; +%template(AuthCredInfoVector) std::vector; +%template(SrtpCryptoVector) std::vector; +%template(SipMultipartPartVector) std::vector; +%template(BuddyVector) std::vector; +%template(BuddyVector2) std::vector; +%template(AudioMediaVector) std::vector; +%template(AudioMediaVector2) std::vector; +%template(VideoMediaVector) std::vector; +%template(ToneDescVector) std::vector; +%template(ToneDigitVector) std::vector; +%template(ToneDigitMapVector) std::vector; +%template(AudioDevInfoVector) std::vector; +%template(AudioDevInfoVector2) std::vector; +%template(CodecInfoVector) std::vector; +%template(CodecInfoVector2) std::vector; +%template(VideoDevInfoVector) std::vector; +%template(VideoDevInfoVector2) std::vector; +%template(CodecFmtpVector) std::vector; %template(MediaFormatAudioVector) std::vector; %template(MediaFormatVideoVector) std::vector; %template(CallMediaInfoVector) std::vector; @@ -213,9 +224,9 @@ using namespace pj; void setWindow(jobject surface) { $self->window = surface; } } #else -%extend pj::WindowHandle { - void setWindow(long long hwnd) { $self->window = (void*)hwnd; } -} +//%extend pj::WindowHandle { +// void setWindow(long long hwnd) { $self->window = (void*)hwnd; } +//} #endif %include "pjsua2/media.hpp" @@ -234,13 +245,13 @@ using namespace pj; %rename(libDestroy_) pj::Endpoint::libDestroy; %typemap(javacode) pj::Endpoint %{ public void libDestroy(long prmFlags) throws java.lang.Exception { - Runtime.getRuntime().gc(); - libDestroy_(prmFlags); + Runtime.getRuntime().gc(); + libDestroy_(prmFlags); } public void libDestroy() throws java.lang.Exception { - Runtime.getRuntime().gc(); - libDestroy_(); + Runtime.getRuntime().gc(); + libDestroy_(); } %} #endif diff --git a/pjsip-apps/src/swig/symbols.i b/pjsip-apps/src/swig/symbols.i index f441522d74..8f26b22be6 100644 --- a/pjsip-apps/src/swig/symbols.i +++ b/pjsip-apps/src/swig/symbols.i @@ -1,22 +1,5 @@ // This file is autogenerated by importsym script, do not modify! -typedef int pj_status_t; - -enum pj_constants_ -{ - PJ_SUCCESS = 0, - PJ_TRUE = 1, - PJ_FALSE = 0 -}; - -typedef unsigned char pj_uint8_t; - -typedef int pj_int32_t; - -typedef unsigned int pj_uint32_t; - -typedef unsigned short pj_uint16_t; - enum pj_file_access { PJ_O_RDONLY = 0x1101, @@ -118,6 +101,15 @@ typedef enum pj_ssl_cipher PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x0000003A, PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x0000006C, PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x0000006D, + PJ_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0x0000c02c, + PJ_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0x0000c030, + PJ_TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x000000a3, + PJ_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x0000009f, + PJ_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0x0000c02b, + PJ_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0x0000c02f, + PJ_TLS_AES_128_GCM_SHA256 = 0x00001301, + PJ_TLS_AES_256_GCM_SHA384 = 0x00001302, + PJ_TLS_CHACHA20_POLY1305_SHA256 = 0x00001303, PJ_TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x00000003, PJ_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x00000006, PJ_TLS_RSA_WITH_IDEA_CBC_SHA = 0x00000007, @@ -156,7 +148,7 @@ typedef enum pj_ssl_sock_proto PJ_SSL_SOCK_PROTO_TLS1_2 = 1 << 4, PJ_SSL_SOCK_PROTO_TLS1_3 = 1 << 5, PJ_SSL_SOCK_PROTO_SSL23 = (1 << 16) - 1, - PJ_SSL_SOCK_PROTO_ALL = PJ_SSL_SOCK_PROTO_SSL23, + PJ_SSL_SOCK_PROTO_ALL = (1 << 16) - 1, PJ_SSL_SOCK_PROTO_DTLS1 = 1 << 16 } pj_ssl_sock_proto; @@ -194,6 +186,23 @@ typedef enum pj_ssl_cert_lookup_type PJ_SSL_CERT_LOOKUP_FRIENDLY_NAME } pj_ssl_cert_lookup_type; +typedef int pj_status_t; + +enum pj_constants_ +{ + PJ_SUCCESS = 0, + PJ_TRUE = 1, + PJ_FALSE = 0 +}; + +typedef unsigned char pj_uint8_t; + +typedef int pj_int32_t; + +typedef unsigned int pj_uint32_t; + +typedef unsigned short pj_uint16_t; + typedef enum pj_ice_sess_trickle { PJ_ICE_SESS_TRICKLE_DISABLED, @@ -259,105 +268,70 @@ typedef enum pjmedia_event_type PJMEDIA_EVENT_CALLBACK = (((' ' << 24) | (' ' << 16)) | ('B' << 8)) | 'C' } pjmedia_event_type; -typedef enum pjmedia_srtp_use -{ - PJMEDIA_SRTP_DISABLED, - PJMEDIA_SRTP_UNKNOWN = PJMEDIA_SRTP_DISABLED, - PJMEDIA_SRTP_OPTIONAL, - PJMEDIA_SRTP_MANDATORY -} pjmedia_srtp_use; - -typedef enum pjmedia_srtp_crypto_option -{ - PJMEDIA_SRTP_NO_ENCRYPTION = 1, - PJMEDIA_SRTP_NO_AUTHENTICATION = 2 -} pjmedia_srtp_crypto_option; - -typedef enum pjmedia_srtp_keying_method -{ - PJMEDIA_SRTP_KEYING_SDES, - PJMEDIA_SRTP_KEYING_DTLS_SRTP, - PJMEDIA_SRTP_KEYINGS_COUNT -} pjmedia_srtp_keying_method; - -typedef enum pjmedia_vid_stream_rc_method -{ - PJMEDIA_VID_STREAM_RC_NONE = 0, - PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING = 1 -} pjmedia_vid_stream_rc_method; - -typedef pj_int32_t pjmedia_vid_dev_index; - -enum pjmedia_vid_dev_std_index -{ - PJMEDIA_VID_DEFAULT_CAPTURE_DEV = -1, - PJMEDIA_VID_DEFAULT_RENDER_DEV = -2, - PJMEDIA_VID_INVALID_DEV = -3 -}; - -typedef enum pjmedia_vid_dev_cap -{ - PJMEDIA_VID_DEV_CAP_FORMAT = 1, - PJMEDIA_VID_DEV_CAP_INPUT_SCALE = 2, - PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW = 4, - PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE = 8, - PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION = 16, - PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE = 32, - PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW = 64, - PJMEDIA_VID_DEV_CAP_ORIENTATION = 128, - PJMEDIA_VID_DEV_CAP_SWITCH = 256, - PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS = 512, - PJMEDIA_VID_DEV_CAP_OUTPUT_FULLSCREEN = 1024, - PJMEDIA_VID_DEV_CAP_MAX = 16384 -} pjmedia_vid_dev_cap; - -typedef enum pjmedia_vid_dev_fullscreen_flag -{ - PJMEDIA_VID_DEV_WINDOWED = 0, - PJMEDIA_VID_DEV_FULLSCREEN = 1, - PJMEDIA_VID_DEV_FULLSCREEN_DESKTOP = 2 -} pjmedia_vid_dev_fullscreen_flag; - -typedef enum pjmedia_aud_dev_route +typedef enum pjmedia_format_id { - PJMEDIA_AUD_DEV_ROUTE_DEFAULT = 0, - PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER = 1, - PJMEDIA_AUD_DEV_ROUTE_EARPIECE = 2, - PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH = 4, - PJMEDIA_AUD_DEV_ROUTE_CUSTOM = 128 -} pjmedia_aud_dev_route; + PJMEDIA_FORMAT_L16 = 0, + PJMEDIA_FORMAT_PCM = PJMEDIA_FORMAT_L16, + PJMEDIA_FORMAT_PCMA = ((('W' << 24) | ('A' << 16)) | ('L' << 8)) | 'A', + PJMEDIA_FORMAT_ALAW = PJMEDIA_FORMAT_PCMA, + PJMEDIA_FORMAT_PCMU = ((('W' << 24) | ('A' << 16)) | ('L' << 8)) | 'u', + PJMEDIA_FORMAT_ULAW = PJMEDIA_FORMAT_PCMU, + PJMEDIA_FORMAT_AMR = ((('R' << 24) | ('M' << 16)) | ('A' << 8)) | ' ', + PJMEDIA_FORMAT_G729 = ((('9' << 24) | ('2' << 16)) | ('7' << 8)) | 'G', + PJMEDIA_FORMAT_ILBC = ((('C' << 24) | ('B' << 16)) | ('L' << 8)) | 'I', + PJMEDIA_FORMAT_RGB24 = ((('3' << 24) | ('B' << 16)) | ('G' << 8)) | 'R', + PJMEDIA_FORMAT_RGBA = ((('A' << 24) | ('B' << 16)) | ('G' << 8)) | 'R', + PJMEDIA_FORMAT_BGRA = ((('A' << 24) | ('R' << 16)) | ('G' << 8)) | 'B', + PJMEDIA_FORMAT_RGB32 = PJMEDIA_FORMAT_RGBA, + PJMEDIA_FORMAT_DIB = (((' ' << 24) | ('B' << 16)) | ('I' << 8)) | 'D', + PJMEDIA_FORMAT_GBRP = ((('P' << 24) | ('R' << 16)) | ('B' << 8)) | 'G', + PJMEDIA_FORMAT_AYUV = ((('V' << 24) | ('U' << 16)) | ('Y' << 8)) | 'A', + PJMEDIA_FORMAT_YUY2 = ((('2' << 24) | ('Y' << 16)) | ('U' << 8)) | 'Y', + PJMEDIA_FORMAT_UYVY = ((('Y' << 24) | ('V' << 16)) | ('Y' << 8)) | 'U', + PJMEDIA_FORMAT_YVYU = ((('U' << 24) | ('Y' << 16)) | ('V' << 8)) | 'Y', + PJMEDIA_FORMAT_I420 = ((('0' << 24) | ('2' << 16)) | ('4' << 8)) | 'I', + PJMEDIA_FORMAT_IYUV = PJMEDIA_FORMAT_I420, + PJMEDIA_FORMAT_YV12 = ((('2' << 24) | ('1' << 16)) | ('V' << 8)) | 'Y', + PJMEDIA_FORMAT_NV12 = ((('2' << 24) | ('1' << 16)) | ('V' << 8)) | 'N', + PJMEDIA_FORMAT_NV21 = ((('1' << 24) | ('2' << 16)) | ('V' << 8)) | 'N', + PJMEDIA_FORMAT_I422 = ((('2' << 24) | ('2' << 16)) | ('4' << 8)) | 'I', + PJMEDIA_FORMAT_I420JPEG = ((('0' << 24) | ('2' << 16)) | ('4' << 8)) | 'J', + PJMEDIA_FORMAT_I422JPEG = ((('2' << 24) | ('2' << 16)) | ('4' << 8)) | 'J', + PJMEDIA_FORMAT_H261 = ((('1' << 24) | ('6' << 16)) | ('2' << 8)) | 'H', + PJMEDIA_FORMAT_H263 = ((('3' << 24) | ('6' << 16)) | ('2' << 8)) | 'H', + PJMEDIA_FORMAT_H263P = ((('3' << 24) | ('6' << 16)) | ('2' << 8)) | 'P', + PJMEDIA_FORMAT_H264 = ((('4' << 24) | ('6' << 16)) | ('2' << 8)) | 'H', + PJMEDIA_FORMAT_VP8 = ((('0' << 24) | ('8' << 16)) | ('P' << 8)) | 'V', + PJMEDIA_FORMAT_VP9 = ((('0' << 24) | ('9' << 16)) | ('P' << 8)) | 'V', + PJMEDIA_FORMAT_MJPEG = ((('G' << 24) | ('P' << 16)) | ('J' << 8)) | 'M', + PJMEDIA_FORMAT_MPEG1VIDEO = ((('V' << 24) | ('1' << 16)) | ('P' << 8)) | 'M', + PJMEDIA_FORMAT_MPEG2VIDEO = ((('V' << 24) | ('2' << 16)) | ('P' << 8)) | 'M', + PJMEDIA_FORMAT_MPEG4 = ((('4' << 24) | ('G' << 16)) | ('P' << 8)) | 'M', + PJMEDIA_FORMAT_INVALID = 0xFFFFFFF +} pjmedia_format_id; -typedef enum pjmedia_aud_dev_cap +typedef enum pjmedia_frame_type { - PJMEDIA_AUD_DEV_CAP_EXT_FORMAT = 1, - PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY = 2, - PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY = 4, - PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING = 8, - PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING = 16, - PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER = 32, - PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER = 64, - PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE = 128, - PJMEDIA_AUD_DEV_CAP_INPUT_SOURCE = 128, - PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE = 256, - PJMEDIA_AUD_DEV_CAP_EC = 512, - PJMEDIA_AUD_DEV_CAP_EC_TAIL = 1024, - PJMEDIA_AUD_DEV_CAP_VAD = 2048, - PJMEDIA_AUD_DEV_CAP_CNG = 4096, - PJMEDIA_AUD_DEV_CAP_PLC = 8192, - PJMEDIA_AUD_DEV_CAP_MAX = 16384 -} pjmedia_aud_dev_cap; + PJMEDIA_FRAME_TYPE_NONE, + PJMEDIA_FRAME_TYPE_AUDIO, + PJMEDIA_FRAME_TYPE_EXTENDED, + PJMEDIA_FRAME_TYPE_VIDEO +} pjmedia_frame_type; -enum pjmedia_file_writer_option +typedef enum pjmedia_jb_discard_algo { - PJMEDIA_FILE_WRITE_PCM = 0, - PJMEDIA_FILE_WRITE_ALAW = 1, - PJMEDIA_FILE_WRITE_ULAW = 2 -}; + PJMEDIA_JB_DISCARD_NONE = 0, + PJMEDIA_JB_DISCARD_STATIC, + PJMEDIA_JB_DISCARD_PROGRESSIVE +} pjmedia_jb_discard_algo; -enum pjmedia_file_player_option +typedef enum pjmedia_rtcp_fb_type { - PJMEDIA_FILE_NO_LOOP = 1 -}; + PJMEDIA_RTCP_FB_ACK, + PJMEDIA_RTCP_FB_NACK, + PJMEDIA_RTCP_FB_TRR_INT, + PJMEDIA_RTCP_FB_OTHER +} pjmedia_rtcp_fb_type; typedef struct pjmedia_tone_digit { @@ -388,6 +362,27 @@ typedef struct pjmedia_tone_desc short flags; } pjmedia_tone_desc; +typedef enum pjmedia_srtp_use +{ + PJMEDIA_SRTP_DISABLED, + PJMEDIA_SRTP_UNKNOWN = PJMEDIA_SRTP_DISABLED, + PJMEDIA_SRTP_OPTIONAL, + PJMEDIA_SRTP_MANDATORY +} pjmedia_srtp_use; + +typedef enum pjmedia_srtp_crypto_option +{ + PJMEDIA_SRTP_NO_ENCRYPTION = 1, + PJMEDIA_SRTP_NO_AUTHENTICATION = 2 +} pjmedia_srtp_crypto_option; + +typedef enum pjmedia_srtp_keying_method +{ + PJMEDIA_SRTP_KEYING_SDES, + PJMEDIA_SRTP_KEYING_DTLS_SRTP, + PJMEDIA_SRTP_KEYINGS_COUNT +} pjmedia_srtp_keying_method; + typedef enum pjmedia_type { PJMEDIA_TYPE_NONE, @@ -437,55 +432,14 @@ typedef enum pjmedia_orient PJMEDIA_ORIENT_ROTATE_270DEG } pjmedia_orient; -typedef enum pjmedia_frame_type +typedef enum pjmedia_vid_dev_hwnd_type { - PJMEDIA_FRAME_TYPE_NONE, - PJMEDIA_FRAME_TYPE_AUDIO, - PJMEDIA_FRAME_TYPE_EXTENDED, - PJMEDIA_FRAME_TYPE_VIDEO -} pjmedia_frame_type; - -typedef enum pjmedia_format_id -{ - PJMEDIA_FORMAT_L16 = 0, - PJMEDIA_FORMAT_PCM = PJMEDIA_FORMAT_L16, - PJMEDIA_FORMAT_PCMA = ((('W' << 24) | ('A' << 16)) | ('L' << 8)) | 'A', - PJMEDIA_FORMAT_ALAW = PJMEDIA_FORMAT_PCMA, - PJMEDIA_FORMAT_PCMU = ((('W' << 24) | ('A' << 16)) | ('L' << 8)) | 'u', - PJMEDIA_FORMAT_ULAW = PJMEDIA_FORMAT_PCMU, - PJMEDIA_FORMAT_AMR = ((('R' << 24) | ('M' << 16)) | ('A' << 8)) | ' ', - PJMEDIA_FORMAT_G729 = ((('9' << 24) | ('2' << 16)) | ('7' << 8)) | 'G', - PJMEDIA_FORMAT_ILBC = ((('C' << 24) | ('B' << 16)) | ('L' << 8)) | 'I', - PJMEDIA_FORMAT_RGB24 = ((('3' << 24) | ('B' << 16)) | ('G' << 8)) | 'R', - PJMEDIA_FORMAT_RGBA = ((('A' << 24) | ('B' << 16)) | ('G' << 8)) | 'R', - PJMEDIA_FORMAT_BGRA = ((('A' << 24) | ('R' << 16)) | ('G' << 8)) | 'B', - PJMEDIA_FORMAT_RGB32 = PJMEDIA_FORMAT_RGBA, - PJMEDIA_FORMAT_DIB = (((' ' << 24) | ('B' << 16)) | ('I' << 8)) | 'D', - PJMEDIA_FORMAT_GBRP = ((('P' << 24) | ('R' << 16)) | ('B' << 8)) | 'G', - PJMEDIA_FORMAT_AYUV = ((('V' << 24) | ('U' << 16)) | ('Y' << 8)) | 'A', - PJMEDIA_FORMAT_YUY2 = ((('2' << 24) | ('Y' << 16)) | ('U' << 8)) | 'Y', - PJMEDIA_FORMAT_UYVY = ((('Y' << 24) | ('V' << 16)) | ('Y' << 8)) | 'U', - PJMEDIA_FORMAT_YVYU = ((('U' << 24) | ('Y' << 16)) | ('V' << 8)) | 'Y', - PJMEDIA_FORMAT_I420 = ((('0' << 24) | ('2' << 16)) | ('4' << 8)) | 'I', - PJMEDIA_FORMAT_IYUV = PJMEDIA_FORMAT_I420, - PJMEDIA_FORMAT_YV12 = ((('2' << 24) | ('1' << 16)) | ('V' << 8)) | 'Y', - PJMEDIA_FORMAT_NV12 = ((('2' << 24) | ('1' << 16)) | ('V' << 8)) | 'N', - PJMEDIA_FORMAT_NV21 = ((('1' << 24) | ('2' << 16)) | ('V' << 8)) | 'N', - PJMEDIA_FORMAT_I422 = ((('2' << 24) | ('2' << 16)) | ('4' << 8)) | 'I', - PJMEDIA_FORMAT_I420JPEG = ((('0' << 24) | ('2' << 16)) | ('4' << 8)) | 'J', - PJMEDIA_FORMAT_I422JPEG = ((('2' << 24) | ('2' << 16)) | ('4' << 8)) | 'J', - PJMEDIA_FORMAT_H261 = ((('1' << 24) | ('6' << 16)) | ('2' << 8)) | 'H', - PJMEDIA_FORMAT_H263 = ((('3' << 24) | ('6' << 16)) | ('2' << 8)) | 'H', - PJMEDIA_FORMAT_H263P = ((('3' << 24) | ('6' << 16)) | ('2' << 8)) | 'P', - PJMEDIA_FORMAT_H264 = ((('4' << 24) | ('6' << 16)) | ('2' << 8)) | 'H', - PJMEDIA_FORMAT_VP8 = ((('0' << 24) | ('8' << 16)) | ('P' << 8)) | 'V', - PJMEDIA_FORMAT_VP9 = ((('0' << 24) | ('9' << 16)) | ('P' << 8)) | 'V', - PJMEDIA_FORMAT_MJPEG = ((('G' << 24) | ('P' << 16)) | ('J' << 8)) | 'M', - PJMEDIA_FORMAT_MPEG1VIDEO = ((('V' << 24) | ('1' << 16)) | ('P' << 8)) | 'M', - PJMEDIA_FORMAT_MPEG2VIDEO = ((('V' << 24) | ('2' << 16)) | ('P' << 8)) | 'M', - PJMEDIA_FORMAT_MPEG4 = ((('4' << 24) | ('G' << 16)) | ('P' << 8)) | 'M', - PJMEDIA_FORMAT_INVALID = 0xFFFFFFF -} pjmedia_format_id; + PJMEDIA_VID_DEV_HWND_TYPE_NONE, + PJMEDIA_VID_DEV_HWND_TYPE_WINDOWS, + PJMEDIA_VID_DEV_HWND_TYPE_COCOA, + PJMEDIA_VID_DEV_HWND_TYPE_IOS, + PJMEDIA_VID_DEV_HWND_TYPE_ANDROID +} pjmedia_vid_dev_hwnd_type; typedef enum pjmedia_vid_packing { @@ -494,13 +448,84 @@ typedef enum pjmedia_vid_packing PJMEDIA_VID_PACKING_WHOLE = 2 } pjmedia_vid_packing; -typedef enum pjmedia_rtcp_fb_type +typedef enum pjmedia_vid_stream_rc_method { - PJMEDIA_RTCP_FB_ACK, - PJMEDIA_RTCP_FB_NACK, - PJMEDIA_RTCP_FB_TRR_INT, - PJMEDIA_RTCP_FB_OTHER -} pjmedia_rtcp_fb_type; + PJMEDIA_VID_STREAM_RC_NONE = 0, + PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING = 1 +} pjmedia_vid_stream_rc_method; + +enum pjmedia_file_writer_option +{ + PJMEDIA_FILE_WRITE_PCM = 0, + PJMEDIA_FILE_WRITE_ALAW = 1, + PJMEDIA_FILE_WRITE_ULAW = 2 +}; + +enum pjmedia_file_player_option +{ + PJMEDIA_FILE_NO_LOOP = 1 +}; + +typedef enum pjmedia_aud_dev_route +{ + PJMEDIA_AUD_DEV_ROUTE_DEFAULT = 0, + PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER = 1, + PJMEDIA_AUD_DEV_ROUTE_EARPIECE = 2, + PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH = 4, + PJMEDIA_AUD_DEV_ROUTE_CUSTOM = 128 +} pjmedia_aud_dev_route; + +typedef enum pjmedia_aud_dev_cap +{ + PJMEDIA_AUD_DEV_CAP_EXT_FORMAT = 1, + PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY = 2, + PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY = 4, + PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING = 8, + PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING = 16, + PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER = 32, + PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER = 64, + PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE = 128, + PJMEDIA_AUD_DEV_CAP_INPUT_SOURCE = 128, + PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE = 256, + PJMEDIA_AUD_DEV_CAP_EC = 512, + PJMEDIA_AUD_DEV_CAP_EC_TAIL = 1024, + PJMEDIA_AUD_DEV_CAP_VAD = 2048, + PJMEDIA_AUD_DEV_CAP_CNG = 4096, + PJMEDIA_AUD_DEV_CAP_PLC = 8192, + PJMEDIA_AUD_DEV_CAP_MAX = 16384 +} pjmedia_aud_dev_cap; + +typedef pj_int32_t pjmedia_vid_dev_index; + +enum pjmedia_vid_dev_std_index +{ + PJMEDIA_VID_DEFAULT_CAPTURE_DEV = -1, + PJMEDIA_VID_DEFAULT_RENDER_DEV = -2, + PJMEDIA_VID_INVALID_DEV = -3 +}; + +typedef enum pjmedia_vid_dev_cap +{ + PJMEDIA_VID_DEV_CAP_FORMAT = 1, + PJMEDIA_VID_DEV_CAP_INPUT_SCALE = 2, + PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW = 4, + PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE = 8, + PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION = 16, + PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE = 32, + PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW = 64, + PJMEDIA_VID_DEV_CAP_ORIENTATION = 128, + PJMEDIA_VID_DEV_CAP_SWITCH = 256, + PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS = 512, + PJMEDIA_VID_DEV_CAP_OUTPUT_FULLSCREEN = 1024, + PJMEDIA_VID_DEV_CAP_MAX = 16384 +} pjmedia_vid_dev_cap; + +typedef enum pjmedia_vid_dev_fullscreen_flag +{ + PJMEDIA_VID_DEV_WINDOWED = 0, + PJMEDIA_VID_DEV_FULLSCREEN = 1, + PJMEDIA_VID_DEV_FULLSCREEN_DESKTOP = 2 +} pjmedia_vid_dev_fullscreen_flag; typedef enum pjsip_cred_data_type { @@ -672,6 +697,19 @@ typedef enum pjsip_hdr_e PJSIP_H_OTHER } pjsip_hdr_e; +typedef enum pjsip_tsx_state_e +{ + PJSIP_TSX_STATE_NULL, + PJSIP_TSX_STATE_CALLING, + PJSIP_TSX_STATE_TRYING, + PJSIP_TSX_STATE_PROCEEDING, + PJSIP_TSX_STATE_COMPLETED, + PJSIP_TSX_STATE_CONFIRMED, + PJSIP_TSX_STATE_TERMINATED, + PJSIP_TSX_STATE_DESTROYED, + PJSIP_TSX_STATE_MAX +} pjsip_tsx_state_e; + typedef enum pjsip_transport_type_e { PJSIP_TRANSPORT_UNSPECIFIED, @@ -717,19 +755,6 @@ typedef enum pjsip_ssl_method PJSIP_SSLV23_METHOD = 23 } pjsip_ssl_method; -typedef enum pjsip_tsx_state_e -{ - PJSIP_TSX_STATE_NULL, - PJSIP_TSX_STATE_CALLING, - PJSIP_TSX_STATE_TRYING, - PJSIP_TSX_STATE_PROCEEDING, - PJSIP_TSX_STATE_COMPLETED, - PJSIP_TSX_STATE_CONFIRMED, - PJSIP_TSX_STATE_TERMINATED, - PJSIP_TSX_STATE_DESTROYED, - PJSIP_TSX_STATE_MAX -} pjsip_tsx_state_e; - typedef enum pjsip_role_e { PJSIP_ROLE_UAC, @@ -747,13 +772,6 @@ typedef enum pjsip_redirect_op PJSIP_REDIRECT_STOP } pjsip_redirect_op; -typedef enum pjrpid_activity -{ - PJRPID_ACTIVITY_UNKNOWN, - PJRPID_ACTIVITY_AWAY, - PJRPID_ACTIVITY_BUSY -} pjrpid_activity; - typedef enum pjsip_evsub_state { PJSIP_EVSUB_STATE_NULL, @@ -765,6 +783,13 @@ typedef enum pjsip_evsub_state PJSIP_EVSUB_STATE_UNKNOWN } pjsip_evsub_state; +typedef enum pjrpid_activity +{ + PJRPID_ACTIVITY_UNKNOWN, + PJRPID_ACTIVITY_AWAY, + PJRPID_ACTIVITY_BUSY +} pjrpid_activity; + typedef enum pjsip_inv_state { PJSIP_INV_STATE_NULL, @@ -947,3 +972,10 @@ typedef enum pjsua_dtmf_method PJSUA_DTMF_METHOD_SIP_INFO } pjsua_dtmf_method; +typedef enum pjsua_sip_siprec_use +{ + PJSUA_SIP_SIPREC_INACTIVE, + PJSUA_SIP_SIPREC_OPTIONAL, + PJSUA_SIP_SIPREC_MANDATORY +} pjsua_sip_siprec_use; + diff --git a/pjsip-apps/src/swig/symbols.lst b/pjsip-apps/src/swig/symbols.lst index 8c3237bc5a..4ddc567c58 100644 --- a/pjsip-apps/src/swig/symbols.lst +++ b/pjsip-apps/src/swig/symbols.lst @@ -1,40 +1,44 @@ -pj/types.h pj_status_t pj_constants_ pj_uint8_t pj_int32_t pj_uint32_t pj_uint16_t -pj/file_io.h pj_file_access -pj/log.h pj_log_decoration -pj/sock_qos.h pj_qos_type pj_qos_flag pj_qos_wmm_prio pj_qos_params -pj/ssl_sock.h pj_ssl_cipher pj_ssl_sock_proto pj_ssl_cert_name_type pj_ssl_cert_verify_flag_t pj_ssl_cert_lookup_type +pj/file_io.h pj_file_access +pj/log.h pj_log_decoration +pj/sock_qos.h pj_qos_type pj_qos_flag pj_qos_wmm_prio pj_qos_params +pj/ssl_sock.h pj_ssl_cipher pj_ssl_sock_proto pj_ssl_cert_name_type pj_ssl_cert_verify_flag_t pj_ssl_cert_lookup_type +pj/types.h pj_status_t pj_constants_ pj_uint8_t pj_int32_t pj_uint32_t pj_uint16_t -pjnath/ice_session.h pj_ice_sess_trickle -pjnath/nat_detect.h pj_stun_nat_type -pjnath/turn_session.h pj_turn_tp_type +pjnath/ice_session.h pj_ice_sess_trickle +pjnath/nat_detect.h pj_stun_nat_type +pjnath/turn_session.h pj_turn_tp_type pjmedia/echo.h pjmedia_echo_flag pjmedia/event.h pjmedia_event_type -pjmedia/transport_srtp.h pjmedia_srtp_use pjmedia_srtp_crypto_option pjmedia_srtp_keying_method -pjmedia/vid_stream.h pjmedia_vid_stream_rc_method -pjmedia-videodev/videodev.h pjmedia_vid_dev_index pjmedia_vid_dev_std_index pjmedia_vid_dev_cap pjmedia_vid_dev_fullscreen_flag -pjmedia-audiodev/audiodev.h pjmedia_aud_dev_route pjmedia_aud_dev_cap -pjmedia/wav_port.h pjmedia_file_writer_option pjmedia_file_player_option -pjmedia/tonegen.h pjmedia_tone_digit pjmedia_tone_digit_map pjmedia_tone_desc +pjmedia/format.h pjmedia_format_id +pjmedia/frame.h pjmedia_frame_type +pjmedia/jbuf.h pjmedia_jb_discard_algo +pjmedia/rtcp_fb.h pjmedia_rtcp_fb_type +pjmedia/tonegen.h pjmedia_tone_digit pjmedia_tone_digit_map pjmedia_tone_desc +pjmedia/transport_srtp.h pjmedia_srtp_use pjmedia_srtp_crypto_option pjmedia_srtp_keying_method pjmedia/types.h pjmedia_type pjmedia_dir pjmedia_tp_proto pjmedia_orient -pjmedia/frame.h pjmedia_frame_type -pjmedia/format.h pjmedia_format_id -pjmedia/vid_codec.h pjmedia_vid_packing -pjmedia/rtcp_fb.h pjmedia_rtcp_fb_type +pjmedia/videodev.h pjmedia_vid_dev_hwnd_type +pjmedia/vid_codec.h pjmedia_vid_packing +pjmedia/vid_stream.h pjmedia_vid_stream_rc_method +pjmedia/wav_port.h pjmedia_file_writer_option pjmedia_file_player_option + +pjmedia-audiodev/audiodev.h pjmedia_aud_dev_route pjmedia_aud_dev_cap + +pjmedia-videodev/videodev.h pjmedia_vid_dev_index pjmedia_vid_dev_std_index pjmedia_vid_dev_cap pjmedia_vid_dev_fullscreen_flag -pjsip/sip_auth.h pjsip_cred_data_type pjsip_auth_algorithm_type +pjsip/sip_auth.h pjsip_cred_data_type pjsip_auth_algorithm_type pjsip/sip_dialog.h pjsip_dialog_cap_status pjsip/sip_event.h pjsip_event_id_e -pjsip/sip_msg.h pjsip_status_code pjsip_hdr_e -pjsip/sip_transport.h pjsip_transport_type_e pjsip_transport_flags_e pjsip_transport_state -pjsip/sip_transport_tls.h pjsip_ssl_method +pjsip/sip_msg.h pjsip_status_code pjsip_hdr_e pjsip/sip_transaction.h pjsip_tsx_state_e +pjsip/sip_transport.h pjsip_transport_type_e pjsip_transport_flags_e pjsip_transport_state +pjsip/sip_transport_tls.h pjsip_ssl_method pjsip/sip_types.h pjsip_role_e pjsip/sip_util.h pjsip_redirect_op -pjsip-simple/rpid.h pjrpid_activity -pjsip-simple/evsub.h pjsip_evsub_state +pjsip-simple/evsub.h pjsip_evsub_state +pjsip-simple/rpid.h pjrpid_activity pjsip-ua/sip_inv.h pjsip_inv_state -pjsua-lib/pjsua.h pjsua_invalid_id_const_ pjsua_state pjsua_stun_use pjsua_upnp_use pjsua_call_hold_type pjsua_acc_id pjsua_destroy_flag pjsua_100rel_use pjsua_sip_timer_use pjsua_ipv6_use pjsua_nat64_opt pjsua_buddy_status pjsua_call_media_status pjsua_vid_win_id pjsua_call_id pjsua_med_tp_st pjsua_call_vid_strm_op pjsua_vid_req_keyframe_method pjsua_call_flag pjsua_create_media_transport_flag pjsua_snd_dev_id pjsua_snd_dev_mode pjsua_ip_change_op pjsua_dtmf_method +pjsua-lib/pjsua.h pjsua_invalid_id_const_ pjsua_state pjsua_stun_use pjsua_upnp_use pjsua_call_hold_type pjsua_acc_id pjsua_destroy_flag pjsua_100rel_use pjsua_sip_timer_use pjsua_ipv6_use pjsua_nat64_opt pjsua_buddy_status pjsua_call_media_status pjsua_vid_win_id pjsua_call_id pjsua_med_tp_st pjsua_call_vid_strm_op pjsua_vid_req_keyframe_method pjsua_call_flag pjsua_create_media_transport_flag pjsua_snd_dev_id pjsua_snd_dev_mode pjsua_ip_change_op pjsua_dtmf_method pjsua_sip_siprec_use From f9e56d82b9500f6c9670fc49bcc84e27b1f47ea2 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Wed, 29 Jan 2025 07:42:18 +0100 Subject: [PATCH 176/491] Fix duplicate function name in 100rel docs (#4275) --- pjsip/include/pjsip-ua/sip_100rel.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pjsip/include/pjsip-ua/sip_100rel.h b/pjsip/include/pjsip-ua/sip_100rel.h index 089ab91fd0..500c07db71 100644 --- a/pjsip/include/pjsip-ua/sip_100rel.h +++ b/pjsip/include/pjsip-ua/sip_100rel.h @@ -70,7 +70,7 @@ * For UAS, if it wants to support \a 100rel but not to mandate it, * it must specify #PJSIP_INV_SUPPORT_100REL flag in the \a options * argument when calling #pjsip_inv_verify_request(), and pass the same - * \a options variable when calling #pjsip_inv_verify_request. If UAC had + * \a options variable when calling #pjsip_inv_create_uas. If UAC had * specified \a 100rel in it's list of extensions in \a Require header, * the UAS will send provisional responses reliably. If UAC only listed * \a 100rel in its \a Supported header but not in \a Require header, @@ -105,7 +105,7 @@ * For another requirement, if UAS wants to mandate \a 100rel support, * it can specify #PJSIP_INV_REQUIRE_100REL flag when calling * #pjsip_inv_verify_request(), and pass the \a options when calling - * #pjsip_inv_verify_request. In this case, + * #pjsip_inv_create_uas. In this case, * \a 100rel extension will be used if UAC specifies \a 100rel in its * \a Supported header. If UAC does not list \a 100rel in \a Supported header, * the incoming INVITE request will be rejected with 421 (Extension Required) From dfcfa138d1df7ab892f70837df65dd1f0bbabbd9 Mon Sep 17 00:00:00 2001 From: Tarteszeus <37761609+Tarteszeus@users.noreply.github.com> Date: Thu, 30 Jan 2025 02:42:36 +0100 Subject: [PATCH 177/491] Add queried names to server address record, and add the address record in parameter for on_verify_cb callback (#4256) --- pjsip/include/pjsip/sip_resolve.h | 3 +++ pjsip/include/pjsip/sip_transport_tls.h | 5 ++++ pjsip/src/pjsip/sip_resolve.c | 4 +++ pjsip/src/pjsip/sip_transport_tls.c | 36 ++++++++++++++++++------- pjsip/src/pjsip/sip_util.c | 6 +++++ 5 files changed, 44 insertions(+), 10 deletions(-) diff --git a/pjsip/include/pjsip/sip_resolve.h b/pjsip/include/pjsip/sip_resolve.h index 702365e96d..4c9d66f31f 100644 --- a/pjsip/include/pjsip/sip_resolve.h +++ b/pjsip/include/pjsip/sip_resolve.h @@ -174,6 +174,9 @@ PJ_BEGIN_DECL /** Address records. */ typedef struct pjsip_server_address_record { + /** The queried name. */ + pj_str_t name; + /** Preferable transport to be used to contact this address. */ pjsip_transport_type_e type; diff --git a/pjsip/include/pjsip/sip_transport_tls.h b/pjsip/include/pjsip/sip_transport_tls.h index 8c0d014f35..27e3a0216d 100644 --- a/pjsip/include/pjsip/sip_transport_tls.h +++ b/pjsip/include/pjsip/sip_transport_tls.h @@ -117,6 +117,11 @@ typedef struct pjsip_tls_on_verify_param { */ const pj_sockaddr_t *remote_addr; + /** + * Describes resolved server addresses. + */ + const pjsip_server_addresses *server_addr; + /** * Describes transport direction. */ diff --git a/pjsip/src/pjsip/sip_resolve.c b/pjsip/src/pjsip/sip_resolve.c index b58e67ac6c..da9b8426a0 100644 --- a/pjsip/src/pjsip/sip_resolve.c +++ b/pjsip/src/pjsip/sip_resolve.c @@ -387,6 +387,7 @@ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, pjsip_transport_get_type_name(type), pjsip_transport_get_type_desc(type))); + svr_addr.entry[i].name = target->addr.host; svr_addr.entry[i].priority = 0; svr_addr.entry[i].weight = 0; svr_addr.entry[i].type = type; @@ -571,6 +572,7 @@ static void dns_a_callback(void *user_data, if (rec.addr[i].af != pj_AF_INET()) continue; + srv->entry[srv->count].name = rec.name; srv->entry[srv->count].type = query->naptr[0].type; srv->entry[srv->count].priority = 0; srv->entry[srv->count].weight = 0; @@ -633,6 +635,7 @@ static void dns_aaaa_callback(void *user_data, if (rec.addr[i].af != pj_AF_INET6()) continue; + srv->entry[srv->count].name = rec.name; srv->entry[srv->count].type = query->naptr[0].type | PJSIP_TRANSPORT_IPV6; srv->entry[srv->count].priority = 0; @@ -692,6 +695,7 @@ static void srv_resolver_cb(void *user_data, for (j = 0; j < s->addr_count && srv.count < PJSIP_MAX_RESOLVED_ADDRESSES; ++j) { + srv.entry[srv.count].name = rec->entry[i].server.name; srv.entry[srv.count].type = query->naptr[0].type; srv.entry[srv.count].priority = rec->entry[i].priority; srv.entry[srv.count].weight = rec->entry[i].weight; diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c index 037615d96a..2079351979 100644 --- a/pjsip/src/pjsip/sip_transport_tls.c +++ b/pjsip/src/pjsip/sip_transport_tls.c @@ -87,6 +87,7 @@ struct tls_transport pjsip_transport base; pj_bool_t is_server; pj_str_t remote_name; + pjsip_server_addresses server_addr; pj_bool_t is_registered; pj_bool_t is_closing; @@ -166,6 +167,7 @@ static pj_status_t tls_create(struct tls_listener *listener, const pj_sockaddr *local, const pj_sockaddr *remote, const pj_str_t *remote_name, + const pjsip_server_addresses *addr, pj_grp_lock_t *glock, struct tls_transport **p_tls); @@ -851,6 +853,7 @@ static pj_status_t tls_create( struct tls_listener *listener, const pj_sockaddr *local, const pj_sockaddr *remote, const pj_str_t *remote_name, + const pjsip_server_addresses *addr, pj_grp_lock_t *glock, struct tls_transport **p_tls) { @@ -927,6 +930,14 @@ static pj_status_t tls_create( struct tls_listener *listener, sockaddr_to_host_port(pool, &tls->base.remote_name, remote); } + if (addr) { + pj_memcpy( &tls->server_addr, addr, + sizeof(pjsip_server_addresses)); + for (int i = 0; i < addr->count; ++i) { + pj_strdup(pool, &tls->server_addr.entry[i].name, &addr->entry[i].name); + } + } + tls->base.endpt = listener->endpt; tls->base.tpmgr = listener->tpmgr; tls->base.send_msg = &tls_send_msg; @@ -1202,6 +1213,7 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, pj_ssl_sock_param ssock_param; pj_sockaddr local_addr; pj_str_t remote_name; + pjsip_server_addresses server_addr; pj_status_t status; /* Sanity checks */ @@ -1221,12 +1233,14 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, POOL_TP_INIT, POOL_TP_INC); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); - /* Get remote host name from tdata */ - if (tdata) + /* Get remote host name and DNS queried server addresses from tdata */ + if (tdata) { remote_name = tdata->dest_info.name; - else + server_addr = tdata->dest_info.addr; + } else { pj_bzero(&remote_name, sizeof(remote_name)); - + pj_bzero(&server_addr, sizeof(server_addr)); + } /* Build SSL socket param */ pj_ssl_sock_param_default(&ssock_param); ssock_param.sock_af = (factory->type & PJSIP_TRANSPORT_IPV6) ? @@ -1303,7 +1317,7 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, /* Create the transport descriptor */ status = tls_create(listener, pool, ssock, PJ_FALSE, &local_addr, - rem_addr, &remote_name, glock, &tls); + rem_addr, &remote_name, &server_addr, glock, &tls); if (status != PJ_SUCCESS) return status; @@ -1482,7 +1496,7 @@ static pj_bool_t on_accept_complete2(pj_ssl_sock_t *ssock, * Create TLS transport for the new socket. */ status = tls_create( listener, NULL, new_ssock, PJ_TRUE, - &ssl_info.local_addr, &tmp_src_addr, NULL, + &ssl_info.local_addr, &tmp_src_addr, NULL, NULL, ssl_info.grp_lock, &tls); if (status != PJ_SUCCESS) { @@ -1635,6 +1649,7 @@ static pj_bool_t on_data_sent(pj_ssl_sock_t *ssock, static pj_bool_t on_verify_cb(pj_ssl_sock_t* ssock, pj_bool_t is_server) { pj_bool_t(*verify_cb)(const pjsip_tls_on_verify_param * param) = NULL; + struct tls_transport* tls_trans = NULL; if (is_server) { struct tls_listener* tls; @@ -1642,10 +1657,8 @@ static pj_bool_t on_verify_cb(pj_ssl_sock_t* ssock, pj_bool_t is_server) tls = (struct tls_listener*)pj_ssl_sock_get_user_data(ssock); verify_cb = tls->tls_setting.on_verify_cb; } else { - struct tls_transport* tls; - - tls = (struct tls_transport*)pj_ssl_sock_get_user_data(ssock); - verify_cb = tls->on_verify_cb; + tls_trans = (struct tls_transport*)pj_ssl_sock_get_user_data(ssock); + verify_cb = tls_trans->on_verify_cb; } if (verify_cb) { @@ -1655,6 +1668,9 @@ static pj_bool_t on_verify_cb(pj_ssl_sock_t* ssock, pj_bool_t is_server) pj_bzero(¶m, sizeof(param)); pj_ssl_sock_get_info(ssock, &info); + if (tls_trans) { + param.server_addr = &tls_trans->server_addr; + } param.local_addr = &info.local_addr; param.remote_addr = &info.remote_addr; param.local_cert_info = info.local_cert_info; diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c index 3d5c3912c5..076e643ecf 100644 --- a/pjsip/src/pjsip/sip_util.c +++ b/pjsip/src/pjsip/sip_util.c @@ -1371,6 +1371,9 @@ stateless_send_resolver_callback( pj_status_t status, if (addr && addr != &tdata->dest_info.addr) { pj_memcpy( &tdata->dest_info.addr, addr, sizeof(pjsip_server_addresses)); + for (int i = 0; i < addr->count; ++i) { + pj_strdup(tdata->pool, &tdata->dest_info.addr.entry[i].name, &addr->entry[i].name); + } } pj_assert(tdata->dest_info.addr.count != 0); @@ -1832,6 +1835,9 @@ static void send_response_resolver_cb( pj_status_t status, void *token, /* Update address in send_state. */ pj_memcpy(&send_state->tdata->dest_info.addr, addr, sizeof(*addr)); + for (int i = 0; i < send_state->tdata->dest_info.addr.count; ++i) { + pj_strdup(send_state->tdata->pool, &send_state->tdata->dest_info.addr.entry[i].name, &addr->entry[i].name); + } /* Send response using the transoprt. */ status = pjsip_transport_send( send_state->cur_transport, From 1a4cd67a47deffb9a889cab8d6268bcabc8ce8b3 Mon Sep 17 00:00:00 2001 From: sauwming Date: Thu, 30 Jan 2025 15:21:49 +0800 Subject: [PATCH 178/491] Modify iOS sample apps dev team ID (#4278) --- .../ipjsua-swift.xcodeproj/project.pbxproj | 8 ++++---- .../src/pjsua/ios/ipjsua.xcodeproj/project.pbxproj | 14 +++++++------- .../ios-swift-pjsua2.xcodeproj/project.pbxproj | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pjsip-apps/src/pjsua/ios-swift/ipjsua-swift.xcodeproj/project.pbxproj b/pjsip-apps/src/pjsua/ios-swift/ipjsua-swift.xcodeproj/project.pbxproj index 073a202f53..36a15f70ee 100644 --- a/pjsip-apps/src/pjsua/ios-swift/ipjsua-swift.xcodeproj/project.pbxproj +++ b/pjsip-apps/src/pjsua/ios-swift/ipjsua-swift.xcodeproj/project.pbxproj @@ -553,7 +553,7 @@ CODE_SIGN_STYLE = Manual; DEVELOPMENT_ASSET_PATHS = "\"ipjsua-swift/Preview Content\""; DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 93NHJQ455P; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 3TRN37UBMA; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = "ipjsua-swift/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -595,7 +595,7 @@ CODE_SIGN_STYLE = Manual; DEVELOPMENT_ASSET_PATHS = "\"ipjsua-swift/Preview Content\""; DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 93NHJQ455P; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 3TRN37UBMA; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = "ipjsua-swift/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -635,7 +635,7 @@ CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 93NHJQ455P; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 3TRN37UBMA; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -663,7 +663,7 @@ CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 93NHJQ455P; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 3TRN37UBMA; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; diff --git a/pjsip-apps/src/pjsua/ios/ipjsua.xcodeproj/project.pbxproj b/pjsip-apps/src/pjsua/ios/ipjsua.xcodeproj/project.pbxproj index 9a70996b7f..5af00dac94 100644 --- a/pjsip-apps/src/pjsua/ios/ipjsua.xcodeproj/project.pbxproj +++ b/pjsip-apps/src/pjsua/ios/ipjsua.xcodeproj/project.pbxproj @@ -340,7 +340,7 @@ ORGANIZATIONNAME = Teluu; TargetAttributes = { 3AF0580316F050770046B835 = { - DevelopmentTeam = 93NHJQ455P; + DevelopmentTeam = 3TRN37UBMA; ProvisioningStyle = Manual; }; }; @@ -534,7 +534,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 93NHJQ455P; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 3TRN37UBMA; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "ipjsua/ipjsua-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -564,10 +564,10 @@ ); ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.teluupush.--PRODUCT-NAME-rfc1034identifier-"; - "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.teluupush.ipjsua; + "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.teluupn.ipjsua; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "ipjsua Push"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Teluu PN Profile"; VALID_ARCHS = arm64; WRAPPER_EXTENSION = app; }; @@ -581,7 +581,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 93NHJQ455P; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 3TRN37UBMA; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "ipjsua/ipjsua-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -610,10 +610,10 @@ ); ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.teluupush.--PRODUCT-NAME-rfc1034identifier-"; - "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.teluupush.ipjsua; + "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.teluupn.ipjsua; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "ipjsua Push"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Teluu PN Profile"; VALID_ARCHS = arm64; WRAPPER_EXTENSION = app; }; diff --git a/pjsip-apps/src/pjsua2/ios-swift-pjsua2/ios-swift-pjsua2.xcodeproj/project.pbxproj b/pjsip-apps/src/pjsua2/ios-swift-pjsua2/ios-swift-pjsua2.xcodeproj/project.pbxproj index ac2ed71594..b8b96f4f03 100644 --- a/pjsip-apps/src/pjsua2/ios-swift-pjsua2/ios-swift-pjsua2.xcodeproj/project.pbxproj +++ b/pjsip-apps/src/pjsua2/ios-swift-pjsua2/ios-swift-pjsua2.xcodeproj/project.pbxproj @@ -455,7 +455,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 93NHJQ455P; + DEVELOPMENT_TEAM = 3TRN37UBMA; ENABLE_BITCODE = NO; ENABLE_TESTABILITY = YES; GCC_NO_COMMON_BLOCKS = YES; From dae52f60d86fb629f5eb70d87a7447d1713866c0 Mon Sep 17 00:00:00 2001 From: Perry Ismangil Date: Thu, 30 Jan 2025 08:43:54 +0000 Subject: [PATCH 179/491] Fixing typo (#4274) Acoustic --- pjmedia/include/pjmedia/config.h | 4 ++-- pjmedia/include/pjmedia/echo.h | 10 +++++----- pjmedia/include/pjmedia/echo_port.h | 2 +- pjmedia/src/pjmedia/audiodev.c | 2 +- pjsip-apps/build/get-footprint.py | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h index 8408f0ae86..86746c81c1 100644 --- a/pjmedia/include/pjmedia/config.h +++ b/pjmedia/include/pjmedia/config.h @@ -705,7 +705,7 @@ /** - * Speex Accoustic Echo Cancellation (AEC). + * Speex Acoustic Echo Cancellation (AEC). * By default is enabled. */ #ifndef PJMEDIA_HAS_SPEEX_AEC @@ -735,7 +735,7 @@ /** - * WebRtc Accoustic Echo Cancellation (AEC). + * WebRtc Acoustic Echo Cancellation (AEC). * By default is disabled. */ #ifndef PJMEDIA_HAS_WEBRTC_AEC diff --git a/pjmedia/include/pjmedia/echo.h b/pjmedia/include/pjmedia/echo.h index 6d05f7a0f9..9045918217 100644 --- a/pjmedia/include/pjmedia/echo.h +++ b/pjmedia/include/pjmedia/echo.h @@ -29,14 +29,14 @@ /** - * @defgroup PJMEDIA_Echo_Cancel Accoustic Echo Cancellation API + * @defgroup PJMEDIA_Echo_Cancel Acoustic Echo Cancellation API * @ingroup PJMEDIA_PORT * @brief Echo Cancellation API. * @{ * * This section describes API to perform echo cancellation to audio signal. * There may be multiple echo canceller implementation in PJMEDIA, ranging - * from simple echo suppressor to a full Accoustic Echo Canceller/AEC. By + * from simple echo suppressor to a full Acoustic Echo Canceller/AEC. By * using this API, application should be able to use which EC backend to * use base on the requirement and capability of the platform. */ @@ -71,7 +71,7 @@ typedef enum pjmedia_echo_flag /** * If PJMEDIA_ECHO_SIMPLE flag is specified during echo canceller * creation, then a simple echo suppressor will be used instead of - * an accoustic echo cancellation. You can only choose one backend. + * an acoustic echo cancellation. You can only choose one backend. */ PJMEDIA_ECHO_SIMPLE = 2, @@ -268,7 +268,7 @@ PJ_DECL(void) pjmedia_echo_stat_default(pjmedia_echo_stat *stat); * is not known. * @param options Options. If PJMEDIA_ECHO_SIMPLE is specified, * then a simple echo suppressor implementation - * will be used instead of an accoustic echo + * will be used instead of an acoustic echo * cancellation. * See #pjmedia_echo_flag for other options. * @param p_echo Pointer to receive the Echo Canceller state. @@ -296,7 +296,7 @@ PJ_DECL(pj_status_t) pjmedia_echo_create(pj_pool_t *pool, * is not known. * @param options Options. If PJMEDIA_ECHO_SIMPLE is specified, * then a simple echo suppressor implementation - * will be used instead of an accoustic echo + * will be used instead of an acoustic echo * cancellation. * See #pjmedia_echo_flag for other options. * @param p_echo Pointer to receive the Echo Canceller state. diff --git a/pjmedia/include/pjmedia/echo_port.h b/pjmedia/include/pjmedia/echo_port.h index 228acf16d2..10d1b0fd02 100644 --- a/pjmedia/include/pjmedia/echo_port.h +++ b/pjmedia/include/pjmedia/echo_port.h @@ -21,7 +21,7 @@ /** * @file echo_port.h - * @brief AEC (Accoustic Echo Cancellation) media port. + * @brief AEC (Acoustic Echo Cancellation) media port. */ #include diff --git a/pjmedia/src/pjmedia/audiodev.c b/pjmedia/src/pjmedia/audiodev.c index 24e4e48e66..ff7a2650b5 100644 --- a/pjmedia/src/pjmedia/audiodev.c +++ b/pjmedia/src/pjmedia/audiodev.c @@ -43,7 +43,7 @@ static struct cap_info DEFINE_CAP("meter-out", "Output meter"), DEFINE_CAP("route-in", "Input routing"), DEFINE_CAP("route-out", "Output routing"), - DEFINE_CAP("aec", "Accoustic echo cancellation"), + DEFINE_CAP("aec", "Acoustic echo cancellation"), DEFINE_CAP("aec-tail", "Tail length setting for AEC"), DEFINE_CAP("vad", "Voice activity detection"), DEFINE_CAP("cng", "Comfort noise generation"), diff --git a/pjsip-apps/build/get-footprint.py b/pjsip-apps/build/get-footprint.py index 3dbb8c82e6..dc89dfad90 100644 --- a/pjsip-apps/build/get-footprint.py +++ b/pjsip-apps/build/get-footprint.py @@ -83,7 +83,7 @@ ['HAS_PJNATH_ICE', 'PJNATH ICE'], # PJMEDIA - ['HAS_PJMEDIA_EC', 'PJMEDIA accoustic echo cancellation'], + ['HAS_PJMEDIA_EC', 'PJMEDIA acoustic echo cancellation'], ['HAS_PJMEDIA_SND_DEV', 'PJMEDIA sound device backend (platform specific)'], ['HAS_PJMEDIA_SILENCE_DET', 'PJMEDIA Adaptive silence detector'], ['HAS_PJMEDIA', 'PJMEDIA endpoint'], From 727ee325484dccf43cc1431335a2e13f9425a120 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Fri, 31 Jan 2025 09:08:02 +0700 Subject: [PATCH 180/491] Fix build error when PJ_LOG_MAX_LEVEL is zero (#4279) The `pj_log_get_log_func()` is not defined when PJ_LOG_MAX_LEVEL is set to zero. Thanks to Giorgio Alfarano for the report. --- pjlib/include/pj/log.h | 7 +++++++ pjlib/src/pj/unittest.c | 10 +++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/pjlib/include/pj/log.h b/pjlib/include/pj/log.h index 88188725b6..48d0f7b8ee 100644 --- a/pjlib/include/pj/log.h +++ b/pjlib/include/pj/log.h @@ -273,6 +273,13 @@ pj_status_t pj_log_init(void); */ # define pj_log_set_log_func(func) +/** + * Get the current log output function that is used to write log messages. + * + * @return Current log output function. + */ +# define pj_log_get_log_func() NULL + /** * Write to log. * diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index 3536db9949..a4e4ccfcc1 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -31,6 +31,9 @@ static long tls_id = INVALID_TLS_ID; /* When basic runner is used, current test is saved in this global var */ static pj_test_case *tc_main_thread; +/* Is unit test running */ +static pj_bool_t is_under_test; + /* Forward decls. */ static void unittest_log_callback(int level, const char *data, int len); static int get_completion_line( const pj_test_case *tc, const char *end_line, @@ -227,9 +230,11 @@ PJ_DEF(void) pj_test_run(pj_test_runner *runner, pj_test_suite *suite) } /* Call the run method to perform runner specific loop */ + is_under_test = PJ_TRUE; pj_get_timestamp(&suite->start_time); runner->main(runner); pj_get_timestamp(&suite->end_time); + is_under_test = PJ_FALSE; /* Restore logging */ pj_log_set_log_func(runner->orig_log_writer); @@ -238,7 +243,10 @@ PJ_DEF(void) pj_test_run(pj_test_runner *runner, pj_test_suite *suite) /* Check if we are under test */ PJ_DEF(pj_bool_t) pj_test_is_under_test(void) { - return pj_log_get_log_func()==&unittest_log_callback; + // Cannot use pj_log_get_log_func() when PJ_LOG_MAX_LEVEL is 0. + //return pj_log_get_log_func()==&unittest_log_callback; + + return is_under_test; } /* Calculate statistics */ From f986ad87ae1f7aaecce8005802f2f0d436285c8f Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Fri, 31 Jan 2025 09:45:19 +0700 Subject: [PATCH 181/491] Add link to coding style documentation (#4280) --- .github/PULL_REQUEST_TEMPLATE/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md index 4f9ceac420..e843daf0e3 100644 --- a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -23,7 +23,7 @@ ## Checklist: -- [ ] My code follows the code style of this project. +- [ ] My code follows the **[CODING STYLE of this project](https://docs.pjsip.org/en/latest/get-started/coding-style.html)**. - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [ ] I have read the **CONTRIBUTING** document. From 46111c4f0dbad6f1a708cff498233a943af7afd6 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Mon, 3 Feb 2025 11:36:54 +0700 Subject: [PATCH 182/491] Best effort avoid crash when media transport adapter not using group lock (#4281) --- pjmedia/src/pjmedia/stream.c | 6 ++++++ pjsip/src/pjsua-lib/pjsua_media.c | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index bbecb20be0..ac9b0d3e3a 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -3168,6 +3168,12 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) PJ_LOG(4,(stream->port.info.name.ptr, "Stream destroying")); + /* Stop the streaming */ + if (stream->enc) + stream->port.put_frame = NULL; + if (stream->dec) + stream->port.get_frame = NULL; + /* Send RTCP BYE (also SDES & XR) */ if (stream->transport && !stream->rtcp_sdes_bye_disabled) { #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index 9d5dc560ec..bfcd5d0b24 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -2269,6 +2269,7 @@ static pj_status_t media_channel_init_cb(pjsua_call_id call_id, if (call_med->use_custom_med_tp) { unsigned custom_med_tp_flags = PJSUA_MED_TP_CLOSE_MEMBER; + pj_grp_lock_t *tp_grp_lock = call_med->tp->grp_lock; /* Use custom media transport returned by the application */ call_med->tp = @@ -2279,6 +2280,19 @@ static pj_status_t media_channel_init_cb(pjsua_call_id call_id, status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_TEMPORARILY_UNAVAILABLE); } + + /* Check if the media transport adapter has no group lock. */ + if (call_med->tp && call_med->tp->grp_lock==NULL) { + PJ_LOG(3, (THIS_FILE, "Call %d media %d: Warning, " + "media transport adapter should manage its " + "lifetime using group lock of the underlying " + "transport", call_id, mi)); + + /* Assign group lock, this will maintain the lifetime of + * the original transport only, not the transport adapter. + */ + call_med->tp->grp_lock = tp_grp_lock; + } } if (call_med->tp) { From 986fc7847bbe0906c057202782aa4c5d6f75097f Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 3 Feb 2025 08:31:28 +0100 Subject: [PATCH 183/491] Share an auth session between multiple dialogs/regc (#4262) --- pjsip/include/pjsip-ua/sip_regc.h | 14 ++++ pjsip/include/pjsip/sip_auth.h | 20 +++++- pjsip/include/pjsip/sip_dialog.h | 15 ++++- pjsip/include/pjsua-lib/pjsua.h | 13 ++++ pjsip/include/pjsua-lib/pjsua_internal.h | 1 + pjsip/include/pjsua2/account.hpp | 13 ++++ pjsip/src/pjsip-ua/sip_reg.c | 7 +- pjsip/src/pjsip/sip_auth_client.c | 82 +++++++++++++++++++++++- pjsip/src/pjsip/sip_dialog.c | 6 ++ pjsip/src/pjsua-lib/pjsua_acc.c | 8 +++ pjsip/src/pjsua-lib/pjsua_call.c | 4 ++ pjsip/src/pjsua-lib/pjsua_core.c | 1 + pjsip/src/pjsua-lib/pjsua_pres.c | 4 ++ pjsip/src/pjsua2/account.cpp | 4 ++ 14 files changed, 186 insertions(+), 6 deletions(-) diff --git a/pjsip/include/pjsip-ua/sip_regc.h b/pjsip/include/pjsip-ua/sip_regc.h index 2d04f4e2c3..ee001306e6 100644 --- a/pjsip/include/pjsip-ua/sip_regc.h +++ b/pjsip/include/pjsip-ua/sip_regc.h @@ -488,6 +488,20 @@ PJ_DECL(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc, PJ_DECL(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata); +/** + * set a shared auth session to be used by this register client. + * This will try to reuse authorization headers from another source + * (e.g. subscribe dialog). + * + * If available, the internal auth session will be ignored. + * To reset client registration, pass NULL as session parameter. + * + * @param regc The client registration structure. + * @param session Pointer to the external session. + */ +PJ_DECL(pj_status_t) pjsip_regc_set_auth_sess( pjsip_regc *regc, + pjsip_auth_clt_sess *session ); + PJ_END_DECL /** diff --git a/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h index 5e11664d5c..9c956bc8a7 100644 --- a/pjsip/include/pjsip/sip_auth.h +++ b/pjsip/include/pjsip/sip_auth.h @@ -343,7 +343,12 @@ typedef struct pjsip_auth_clt_sess unsigned cred_cnt; /**< Number of credentials. */ pjsip_cred_info *cred_info; /**< Array of credential information*/ pjsip_cached_auth cached_auth; /**< Cached authorization info. */ - + pj_lock_t *lock; /**< Prevent concurrent usage when + using a shared parent. Is only + used in parent. Set up by + pjsip_auth_clt_set_parent */ + struct pjsip_auth_clt_sess *parent; /**< allow a common parent + for multiple sessions. */ } pjsip_auth_clt_sess; @@ -602,6 +607,19 @@ PJ_DECL(pj_status_t) pjsip_auth_srv_init( pj_pool_t *pool, unsigned options ); +/** + * Set a parent session to be used instead of the current. + * This allows a central caching of authorization headers over multiple + * dialogs. + * + * @param sess session that will be delegating the requests. + * @param p parent that will be shared. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjsip_auth_clt_set_parent(pjsip_auth_clt_sess *sess, + const pjsip_auth_clt_sess *p); + /** * This structure describes initialization settings of server authorization * session. diff --git a/pjsip/include/pjsip/sip_dialog.h b/pjsip/include/pjsip/sip_dialog.h index 4833847546..61767dca45 100644 --- a/pjsip/include/pjsip/sip_dialog.h +++ b/pjsip/include/pjsip/sip_dialog.h @@ -903,7 +903,19 @@ PJ_DECL(pj_status_t) pjsip_dlg_update_remote_cap(pjsip_dialog *dlg, const pjsip_msg *msg, pj_bool_t strict); - +/** + * set a shared auth session to be used by this dialog. + * This will try to reuse authorization headers from another source + * (e.g. register). + * + * If available, the internal auth session will be ignored. + * To reset client registration, pass NULL as session parameter. + * + * @param dlg The dialog + * @param session Pointer to the external session + */ +PJ_DECL(pj_status_t) pjsip_dlg_set_auth_sess(pjsip_dialog *dlg, + pjsip_auth_clt_sess *session); /** * @} @@ -927,7 +939,6 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata ); - PJ_END_DECL diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index da555d093a..7c4092e102 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -4661,6 +4661,19 @@ typedef struct pjsua_acc_config */ pj_bool_t enable_rtcp_xr; + /** + * Use a shared authorization session within this account. + * This will use the accounts credentials on outgoing requests, + * so that less 401/407 Responses will be returned. + * + * Needs PJSIP_AUTH_AUTO_SEND_NEXT and PJSIP_AUTH_HEADER_CACHING + * enabled to work properly, and also will grow usage of the used pool for + * the cached headers. + * + * Default: PJ_FALSE + */ + pj_bool_t use_shared_auth; + } pjsua_acc_config; diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index c370cb46f4..b14f658730 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -334,6 +334,7 @@ typedef struct pjsua_acc pjsip_transport_type_e tp_type; /**< Transport type (for local acc or transport binding) */ pjsua_ip_change_op ip_change_op;/**< IP change process progress. */ + pjsip_auth_clt_sess shared_auth_sess; /**< share one auth over all requests */ } pjsua_acc; diff --git a/pjsip/include/pjsua2/account.hpp b/pjsip/include/pjsua2/account.hpp index c7eb021703..458f5644bd 100644 --- a/pjsip/include/pjsua2/account.hpp +++ b/pjsip/include/pjsua2/account.hpp @@ -298,6 +298,19 @@ struct AccountSipConfig : public PersistentObject */ pjsua_ipv6_use ipv6Use; + /** + * Use a shared authorization session within this account. + * This will use the accounts credentials on outgoing requests, + * so that less 401/407 Responses will be returned. + * + * Needs PJSIP_AUTH_AUTO_SEND_NEXT and PJSIP_AUTH_HEADER_CACHING + * enabled to work properly, and also will grow usage of the used pool for + * the cached headers. + * + * Default is disabled/false. + */ + bool useSharedAuth; + public: /** * Read this object from a container node. diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c index ed9ba6d451..7595df3ca6 100644 --- a/pjsip/src/pjsip-ua/sip_reg.c +++ b/pjsip/src/pjsip-ua/sip_reg.c @@ -96,6 +96,7 @@ struct pjsip_regc /* Authorization sessions. */ pjsip_auth_clt_sess auth_sess; + pjsip_auth_clt_sess *ext_auth_sess; /**< User defined auth session. */ /* Auto refresh registration. */ pj_bool_t auto_reg; @@ -1555,4 +1556,8 @@ PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata) return status; } - +PJ_DEF(pj_status_t) pjsip_regc_set_auth_sess( pjsip_regc *regc, + pjsip_auth_clt_sess *session ) { + PJ_ASSERT_RETURN(regc, PJ_EINVAL); + return pjsip_auth_clt_set_parent(®c->auth_sess, session); +} diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c index 989e7eaaf6..a68c7db93f 100644 --- a/pjsip/src/pjsip/sip_auth_client.c +++ b/pjsip/src/pjsip/sip_auth_client.c @@ -31,6 +31,7 @@ #include #include #include +#include #if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \ @@ -105,6 +106,20 @@ const pjsip_auth_algorithm pjsip_auth_algorithms[] = { # define AUTH_TRACE_(expr) #endif +#define DO_ON_PARENT_LOCKED(sess, call) \ + do { \ + pj_status_t on_parent; \ + pj_bool_t with_parent = PJ_FALSE; \ + if (sess->parent) { \ + pj_lock_acquire(sess->parent->lock); \ + with_parent = PJ_TRUE; \ + on_parent = call; \ + pj_lock_release(sess->parent->lock); \ + } \ + if (with_parent) { \ + return on_parent; \ + } \ + } while(0) static void dup_bin(pj_pool_t *pool, pj_str_t *dst, const pj_str_t *src) { @@ -712,6 +727,17 @@ static pjsip_cached_auth *find_cached_auth( pjsip_auth_clt_sess *sess, const pj_str_t *realm, pjsip_auth_algorithm_type algorithm_type) { + pj_bool_t with_parent = PJ_FALSE; + pjsip_cached_auth * pauth = NULL; + if (sess->parent) { + pj_lock_acquire(sess->parent->lock); + pauth = find_cached_auth(sess->parent, realm, algorithm_type); + pj_lock_release(sess->parent->lock); + } + if (pauth != NULL) { + return pauth; + } + pjsip_cached_auth *auth = sess->cached_auth.next; while (auth != &sess->cached_auth) { if (pj_stricmp(&auth->realm, realm) == 0 @@ -734,6 +760,17 @@ static const pjsip_cred_info* auth_find_cred( const pjsip_auth_clt_sess *sess, PJ_UNUSED_ARG(auth_scheme); + pj_bool_t with_parent = PJ_FALSE; + const pjsip_cred_info * ptr = NULL; + if (sess->parent) { + pj_lock_acquire(sess->parent->lock); + ptr = auth_find_cred(sess->parent, realm, auth_scheme, algorithm_type); + pj_lock_release(sess->parent->lock); + } + if (ptr != NULL) { + return ptr; + } + for (i=0; icred_cnt; ++i) { switch(sess->cred_info[i].data_type) { case PJSIP_CRED_DATA_PLAIN_PASSWD: @@ -795,6 +832,21 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_init( pjsip_auth_clt_sess *sess, sess->cred_info = NULL; pj_list_init(&sess->cached_auth); + sess->parent = NULL; + sess->lock = NULL; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pjsip_auth_clt_set_parent(pjsip_auth_clt_sess *sess, + const pjsip_auth_clt_sess *parent) +{ + PJ_ASSERT_RETURN(sess && parent, PJ_EINVAL); + if (parent->lock == NULL) { + pj_lock_create_simple_mutex( parent->pool, + "auth_clt_parent_lock", + &parent->lock ); + } + sess->parent = parent; return PJ_SUCCESS; } @@ -812,7 +864,12 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_deinit(pjsip_auth_clt_sess *sess) auth = auth->next; } - return PJ_SUCCESS; + sess->parent = NULL; + if (sess->lock) { + return pj_lock_destroy(sess->lock); + } else { + return PJ_SUCCESS; + } } @@ -849,6 +906,21 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_clone( pj_pool_t *pool, } } + pj_status_t status; + if (sess->parent) { + pj_lock_acquire(sess->parent->lock); + sess->parent = PJ_POOL_ZALLOC_T(pool, pjsip_auth_clt_sess); + if (sess->parent == NULL) { + status = PJ_ENOMEM; + } else { + status = pjsip_auth_clt_clone(pool, sess->parent, rhs->parent); + } + pj_lock_release(sess->parent->lock); + } + if (status != PJ_SUCCESS) { + return status; + } + /* TODO note: * Cloning the full authentication client is quite a big task. * We do only the necessary bits here, i.e. cloning the credentials. @@ -868,6 +940,7 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess, const pjsip_cred_info *c) { PJ_ASSERT_RETURN(sess && c, PJ_EINVAL); + DO_ON_PARENT_LOCKED(sess, pjsip_auth_clt_set_credentials(sess->parent, cred_cnt, c)); if (cred_cnt == 0) { sess->cred_cnt = 0; @@ -943,6 +1016,7 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_set_prefs(pjsip_auth_clt_sess *sess, const pjsip_auth_clt_pref *p) { PJ_ASSERT_RETURN(sess && p, PJ_EINVAL); + DO_ON_PARENT_LOCKED(sess, pjsip_auth_clt_set_prefs(sess->parent, p)); pj_memcpy(&sess->pref, p, sizeof(*p)); pj_strdup(sess->pool, &sess->pref.algorithm, &p->algorithm); @@ -960,7 +1034,7 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_get_prefs(pjsip_auth_clt_sess *sess, pjsip_auth_clt_pref *p) { PJ_ASSERT_RETURN(sess && p, PJ_EINVAL); - + DO_ON_PARENT_LOCKED(sess, pjsip_auth_clt_get_prefs(sess->parent, p)); pj_memcpy(p, &sess->pref, sizeof(pjsip_auth_clt_pref)); return PJ_SUCCESS; } @@ -1197,6 +1271,8 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess, PJ_ASSERT_RETURN(tdata->msg->type==PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); + + DO_ON_PARENT_LOCKED(sess, pjsip_auth_clt_init_req(sess->parent, tdata)); /* Init list */ pj_list_init(&added); @@ -1548,6 +1624,8 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess, rdata->msg_info.msg->line.status.code == 407, PJSIP_EINVALIDSTATUS); + DO_ON_PARENT_LOCKED(sess, pjsip_auth_clt_reinit_req(sess->parent, rdata, old_request, new_request)); + tdata = old_request; tdata->auth_retry = PJ_FALSE; diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c index 5bb81b820d..ba37efa79b 100644 --- a/pjsip/src/pjsip/sip_dialog.c +++ b/pjsip/src/pjsip/sip_dialog.c @@ -2493,3 +2493,9 @@ PJ_DEF(pj_status_t) pjsip_dlg_remove_remote_cap_hdr(pjsip_dialog *dlg, return PJ_SUCCESS; } + +PJ_DEF(pj_status_t) pjsip_dlg_set_auth_sess( pjsip_dialog *dlg, + pjsip_auth_clt_sess *session ) { + PJ_ASSERT_RETURN(dlg, PJ_EINVAL); + return pjsip_auth_clt_set_parent(&dlg->auth_sess, session); +} diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index 25ea8c1aad..692f6e988e 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -329,6 +329,7 @@ static pj_status_t initialize_acc(unsigned acc_id) } else { sip_reg_uri = NULL; } + pjsip_auth_clt_init( &acc->shared_auth_sess, pjsua_var.endpt, acc->pool, 0); if (sip_reg_uri) { acc->srv_port = sip_reg_uri->port; @@ -1297,6 +1298,9 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id, unreg_first = PJ_TRUE; } + /* Shared authentication session */ + acc->cfg.use_shared_auth = cfg->use_shared_auth; + /* Registration */ if (acc->cfg.reg_timeout != cfg->reg_timeout) { acc->cfg.reg_timeout = cfg->reg_timeout; @@ -2754,6 +2758,10 @@ static pj_status_t pjsua_regc_init(int acc_id) pjsua_init_tpselector(acc_id, &tp_sel); pjsip_regc_set_transport(acc->regc, &tp_sel); + if (acc->cfg.use_shared_auth) { + pjsip_regc_set_auth_sess(acc->regc, &acc->shared_auth_sess); + } + /* Set credentials */ if (acc->cred_cnt) { diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index db020527e5..2b51aa8b87 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -983,6 +983,10 @@ PJ_DEF(pj_status_t) pjsua_call_make_call(pjsua_acc_id acc_id, dlg_set_via(dlg, acc); + if (acc->cfg.use_shared_auth) { + pjsip_dlg_set_auth_sess(dlg, &acc->shared_auth_sess); + } + /* Calculate call's secure level */ call->secure_level = get_secure_level(acc_id, dest_uri); diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 569166eace..176e8d6309 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -387,6 +387,7 @@ PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg) PJSUA_CALL_UPDATE_CONTACT | PJSUA_CALL_UPDATE_VIA; cfg->enable_rtcp_xr = (PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR); + cfg->use_shared_auth = PJ_FALSE; } PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg) diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c index bb5f72526c..3a6fbc3888 100644 --- a/pjsip/src/pjsua-lib/pjsua_pres.c +++ b/pjsip/src/pjsua-lib/pjsua_pres.c @@ -2127,6 +2127,10 @@ static void subscribe_buddy(pjsua_buddy_id buddy_id, pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set); } + if (acc->cfg.use_shared_auth) { + pjsip_dlg_set_auth_sess(buddy->dlg, &acc->shared_auth_sess); + } + /* Set credentials */ if (acc->cred_cnt) { pjsip_auth_clt_set_credentials( &buddy->dlg->auth_sess, diff --git a/pjsip/src/pjsua2/account.cpp b/pjsip/src/pjsua2/account.cpp index a8af6a1e4a..d40a200c3d 100644 --- a/pjsip/src/pjsua2/account.cpp +++ b/pjsip/src/pjsua2/account.cpp @@ -281,6 +281,7 @@ void AccountSipConfig::readObject(const ContainerNode &node) NODE_READ_BOOL (this_node, authInitialEmpty); NODE_READ_STRING (this_node, authInitialAlgorithm); NODE_READ_INT (this_node, transportId); + NODE_READ_BOOL (this_node, useSharedAuth); ContainerNode creds_node = this_node.readArray("authCreds"); authCreds.resize(0); @@ -303,6 +304,7 @@ void AccountSipConfig::writeObject(ContainerNode &node) const NODE_WRITE_BOOL (this_node, authInitialEmpty); NODE_WRITE_STRING (this_node, authInitialAlgorithm); NODE_WRITE_INT (this_node, transportId); + NODE_WRITE_BOOL (this_node, useSharedAuth); ContainerNode creds_node = this_node.writeNewArray("authCreds"); for (unsigned i=0; i Date: Tue, 4 Feb 2025 08:38:55 +0800 Subject: [PATCH 184/491] Fixed CI test failure (#4284) --- .github/workflows/ci-linux.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 0fa25ef527..4f1a7e99bc 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -32,7 +32,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: install dependencies - run: sudo apt-get install -y swig sip-tester libopencore-amrnb-dev + run: sudo apt-get update && sudo apt-get install -y swig sip-tester libopencore-amrnb-dev - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h - name: configure @@ -149,7 +149,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: install dependencies - run: sudo apt-get install -y swig nasm sip-tester libvpx-dev libopencore-amrnb-dev libsdl2-dev + run: sudo apt-get update && sudo apt-get install -y swig nasm sip-tester libvpx-dev libopencore-amrnb-dev libsdl2-dev - name: get openh264 run: git clone --depth 1 --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git - name: build openh264 @@ -196,7 +196,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: install dependencies - run: sudo apt-get install -y nasm libvpx-dev libopencore-amrnb-dev libsdl2-dev + run: sudo apt-get update && sudo apt-get install -y nasm libvpx-dev libopencore-amrnb-dev libsdl2-dev - name: get openh264 run: git clone --depth 1 --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git - name: build openh264 @@ -220,7 +220,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: install dependencies - run: sudo apt-get install -y nasm libvpx-dev libopencore-amrnb-dev libsdl2-dev + run: sudo apt-get update && sudo apt-get install -y nasm libvpx-dev libopencore-amrnb-dev libsdl2-dev - name: get openh264 run: git clone --depth 1 --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git - name: build openh264 @@ -240,7 +240,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: install dependencies - run: sudo apt-get install -y swig nasm libx264-dev libvpx-dev libsdl2-dev + run: sudo apt-get update && sudo apt-get install -y swig nasm libx264-dev libvpx-dev libsdl2-dev - name: get ffmpeg run: git clone --depth 1 --single-branch --branch release/4.2 https://github.com/FFmpeg/FFmpeg.git - name: configure ffmpeg From 205baf0f51856573d5deaba643f7df71f2cc55c0 Mon Sep 17 00:00:00 2001 From: sauwming Date: Tue, 4 Feb 2025 17:04:25 +0800 Subject: [PATCH 185/491] Fixed warnings in sip auth client (#4287) --- pjsip/include/pjsip/sip_auth.h | 2 +- pjsip/src/pjsip/sip_auth_client.c | 24 +++++++++++++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h index 9c956bc8a7..287f668fe8 100644 --- a/pjsip/include/pjsip/sip_auth.h +++ b/pjsip/include/pjsip/sip_auth.h @@ -618,7 +618,7 @@ PJ_DECL(pj_status_t) pjsip_auth_srv_init( pj_pool_t *pool, * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_auth_clt_set_parent(pjsip_auth_clt_sess *sess, - const pjsip_auth_clt_sess *p); + pjsip_auth_clt_sess *p); /** * This structure describes initialization settings of server authorization diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c index a68c7db93f..31bf551e68 100644 --- a/pjsip/src/pjsip/sip_auth_client.c +++ b/pjsip/src/pjsip/sip_auth_client.c @@ -727,7 +727,6 @@ static pjsip_cached_auth *find_cached_auth( pjsip_auth_clt_sess *sess, const pj_str_t *realm, pjsip_auth_algorithm_type algorithm_type) { - pj_bool_t with_parent = PJ_FALSE; pjsip_cached_auth * pauth = NULL; if (sess->parent) { pj_lock_acquire(sess->parent->lock); @@ -757,11 +756,10 @@ static const pjsip_cred_info* auth_find_cred( const pjsip_auth_clt_sess *sess, { unsigned i; int wildcard = -1; + const pjsip_cred_info * ptr = NULL; PJ_UNUSED_ARG(auth_scheme); - pj_bool_t with_parent = PJ_FALSE; - const pjsip_cred_info * ptr = NULL; if (sess->parent) { pj_lock_acquire(sess->parent->lock); ptr = auth_find_cred(sess->parent, realm, auth_scheme, algorithm_type); @@ -838,13 +836,16 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_init( pjsip_auth_clt_sess *sess, } PJ_DEF(pj_status_t) pjsip_auth_clt_set_parent(pjsip_auth_clt_sess *sess, - const pjsip_auth_clt_sess *parent) + pjsip_auth_clt_sess *parent) { PJ_ASSERT_RETURN(sess && parent, PJ_EINVAL); if (parent->lock == NULL) { - pj_lock_create_simple_mutex( parent->pool, - "auth_clt_parent_lock", - &parent->lock ); + pj_status_t status; + status = pj_lock_create_simple_mutex( parent->pool, + "auth_clt_parent_lock", + &parent->lock ); + if (status != PJ_SUCCESS) + return status; } sess->parent = parent; return PJ_SUCCESS; @@ -906,8 +907,9 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_clone( pj_pool_t *pool, } } - pj_status_t status; if (sess->parent) { + pj_status_t status; + pj_lock_acquire(sess->parent->lock); sess->parent = PJ_POOL_ZALLOC_T(pool, pjsip_auth_clt_sess); if (sess->parent == NULL) { @@ -916,9 +918,9 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_clone( pj_pool_t *pool, status = pjsip_auth_clt_clone(pool, sess->parent, rhs->parent); } pj_lock_release(sess->parent->lock); - } - if (status != PJ_SUCCESS) { - return status; + + if (status != PJ_SUCCESS) + return status; } /* TODO note: From cbfbbc47b663c6bf38c0625f4b1189557895ae8d Mon Sep 17 00:00:00 2001 From: jimying Date: Wed, 5 Feb 2025 11:03:53 +0800 Subject: [PATCH 186/491] iocp: fix crash, GetQueuedCompletionStatus() write freed WSAOVERLAPPED memory (#4136) --- .github/workflows/ci-win.yml | 30 + pjlib/build/Makefile | 2 +- pjlib/build/pjlib_test.vcxproj | 1 + pjlib/build/pjlib_test.vcxproj.filters | 3 + pjlib/src/pj/ioqueue_winnt.c | 647 +++++++++++++-------- pjlib/src/pjlib-test/ioq_iocp_unreg_test.c | 208 +++++++ pjlib/src/pjlib-test/ioq_stress_test.c | 19 +- pjlib/src/pjlib-test/ioq_udp.c | 4 +- pjlib/src/pjlib-test/test.c | 4 + pjlib/src/pjlib-test/test.h | 2 + 10 files changed, 674 insertions(+), 246 deletions(-) create mode 100644 pjlib/src/pjlib-test/ioq_iocp_unreg_test.c diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index efc40dcbc1..28e0c5e2bf 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -590,3 +590,33 @@ jobs: set LIB=%LIB%;%FFMPEG_DIR%\lib;%SDL_DIR%\lib\x86 msbuild pjproject-vs14.sln /p:PlatformToolset=v143 /p:Configuration=Release /p:Platform=win32 /p:UseEnv=true shell: cmd + + iocp: + runs-on: windows-latest + name: IOCP / pjlib + steps: + - uses: actions/checkout@master + - name: config site + run: + cd pjlib/include/pj; cp config_site_test.h config_site.h; Add-Content config_site.h "#define PJ_IOQUEUE_IMP PJ_IOQUEUE_IMP_IOCP" + shell: powershell + - name: check VsDevCmd.bat + run: dir "%PROGRAMFILES%\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" + shell: cmd + - name: MSBuild + working-directory: . + run: | + call "%PROGRAMFILES%\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" + msbuild pjproject-vs14.sln /p:PlatformToolset=v143 /p:Configuration=Release /p:Platform=win32 /p:UseEnv=true + shell: cmd + - name: verify ioqueue type is iocp + run: | + cd pjlib/bin + pjlib-test-i386-Win32-vc14-Release.exe -c -L | findstr /C:"ioqueue type" | findstr iocp + shell: cmd + - name: pjlib-test + run: | + cd pjlib/bin + $args = $env:CI_WIN_ARGS -split ' ' + ./pjlib-test-i386-Win32-vc14-Release.exe $args $env:CI_MODE + shell: powershell diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile index a273560e01..caf01121d6 100644 --- a/pjlib/build/Makefile +++ b/pjlib/build/Makefile @@ -47,7 +47,7 @@ export PJLIB_LDFLAGS += $(_LDFLAGS) export TEST_SRCDIR = ../src/pjlib-test export TEST_OBJS += activesock.o atomic.o echo_clt.o errno.o exception.o \ fifobuf.o file.o hash_test.o ioq_perf.o ioq_udp.o \ - ioq_stress_test.o ioq_unreg.o ioq_tcp.o \ + ioq_stress_test.o ioq_unreg.o ioq_tcp.o ioq_iocp_unreg_test.o \ list.o mutex.o os.o pool.o pool_perf.o rand.o rbtree.o \ select.o sleep.o sock.o sock_perf.o ssl_sock.o \ string.o test.o thread.o timer.o timestamp.o \ diff --git a/pjlib/build/pjlib_test.vcxproj b/pjlib/build/pjlib_test.vcxproj index 0801881867..3f78ad2835 100644 --- a/pjlib/build/pjlib_test.vcxproj +++ b/pjlib/build/pjlib_test.vcxproj @@ -717,6 +717,7 @@ + diff --git a/pjlib/build/pjlib_test.vcxproj.filters b/pjlib/build/pjlib_test.vcxproj.filters index 64451af637..e3ad56817b 100644 --- a/pjlib/build/pjlib_test.vcxproj.filters +++ b/pjlib/build/pjlib_test.vcxproj.filters @@ -126,6 +126,9 @@ Source Files + + Source Files + diff --git a/pjlib/src/pj/ioqueue_winnt.c b/pjlib/src/pj/ioqueue_winnt.c index 89e0e95fb4..b7de3ffe65 100644 --- a/pjlib/src/pj/ioqueue_winnt.c +++ b/pjlib/src/pj/ioqueue_winnt.c @@ -28,6 +28,7 @@ #include #include +#define THIS_FILE "ioq_winnt" /* Only build when the backend is Windows I/O Completion Ports. */ #if PJ_IOQUEUE_IMP == PJ_IOQUEUE_IMP_IOCP @@ -48,11 +49,27 @@ /* For GetAcceptExSockaddrs() on MSVC2005 */ #pragma comment(lib, "mswsock.lib") +#if 0 +# define TRACE(args) PJ_LOG(3,args) +#else +# define TRACE(args) +#endif + + /* The address specified in AcceptEx() must be 16 more than the size of * SOCKADDR (source: MSDN). */ #define ACCEPT_ADDR_LEN (sizeof(pj_sockaddr_in)+16) + +/* Timeout for cancelling pending operations in ioqueue destroy. + * Upon ioqueue destroy, all keys must be unregistered and all pending + * operations must be cancelled. As cancelling ops is asynchronous, + * IOCP destroy may need to wait for the maximum time specified here. + */ +#define TIMEOUT_CANCEL_OP 5000 + + typedef struct generic_overlapped { WSAOVERLAPPED overlapped; @@ -89,7 +106,7 @@ typedef struct ioqueue_accept_rec #endif /* - * Structure to hold pending operation key. + * Structure to hold operation key. */ union operation_key { @@ -100,6 +117,18 @@ union operation_key #endif }; +/* + * Pending operation. + * As cancellation of IOCP operation is asynchronous, we cannot use the + * operation key provided by app (pj_ioqueue_op_key_t.internal__). + */ +struct pending_op +{ + PJ_DECL_LIST_MEMBER(struct pending_op); + union operation_key pending_key; + pj_ioqueue_op_key_t *app_op_key; +}; + /* Type of handle in the key. */ enum handle_type { @@ -117,6 +146,7 @@ struct pj_ioqueue_key_t { PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t); + pj_pool_t *pool; pj_ioqueue_t *ioqueue; HANDLE hnd; void *user_data; @@ -129,13 +159,9 @@ struct pj_ioqueue_key_t int connecting; #endif -#if PJ_IOQUEUE_HAS_SAFE_UNREG - pj_atomic_t *ref_count; pj_bool_t closing; - pj_time_val free_time; - pj_mutex_t *mutex; -#endif - + struct pending_op pending_list; + struct pending_op free_pending_list; }; /* @@ -143,17 +169,16 @@ struct pj_ioqueue_key_t */ struct pj_ioqueue_t { + pj_pool_t *pool; pj_ioqueue_cfg cfg; HANDLE iocp; pj_lock_t *lock; pj_bool_t auto_delete_lock; pj_bool_t default_concurrency; + pj_size_t max_fd; -#if PJ_IOQUEUE_HAS_SAFE_UNREG pj_ioqueue_key_t active_list; pj_ioqueue_key_t free_list; - pj_ioqueue_key_t closing_list; -#endif /* These are to keep track of connecting sockets */ #if PJ_HAS_TCP @@ -166,10 +191,27 @@ struct pj_ioqueue_t }; -#if PJ_IOQUEUE_HAS_SAFE_UNREG -/* Prototype */ -static void scan_closing_keys(pj_ioqueue_t *ioqueue); -#endif +/* Dynamic resolution of CancelIoEx(). + * (because older SDKs do not have CancelIoEx()?) + */ +typedef BOOL(WINAPI* FnCancelIoEx)(HANDLE hFile, LPOVERLAPPED lpOverlapped); +static FnCancelIoEx fnCancelIoEx = NULL; + +#define OPKEY_OPERATION(op_key) ((union operation_key*)op_key)->generic.operation + +/* Prototypes of internal functions */ +static void key_on_destroy(void* data); +static void increment_counter(pj_ioqueue_key_t* key); +static void decrement_counter(pj_ioqueue_key_t* key); + + +#define PENDING_OP_POS(op_key) (PJ_ARRAY_SIZE(op_key->internal__) - 1) + +static struct pending_op* get_pending_op(pj_ioqueue_op_key_t *op_key) +{ + return (struct pending_op*) + (op_key->internal__[PENDING_OP_POS(op_key)]); +} #if PJ_HAS_TCP @@ -193,6 +235,7 @@ static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key, SO_UPDATE_ACCEPT_CONTEXT, (char*)&key->hnd, sizeof(SOCKET)); + (void)status; /* SO_UPDATE_ACCEPT_CONTEXT is for WinXP or later. * So ignore the error status. */ @@ -377,12 +420,31 @@ PJ_DEF(pj_status_t) pj_ioqueue_create2(pj_pool_t *pool, rc = sizeof(union operation_key); - /* Check that sizeof(pj_ioqueue_op_key_t) makes sense. */ - PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >= + /* Check that sizeof(pj_ioqueue_op_key_t) makes sense. + * IOCP operations require some buffers (WSAOVERLAPPED, etc) which is + * represented by operation_key. The pj_ioqueue_op_key_t also holds + * three important pointers: activesock_data, user_data, and + * app supplied op-key (at .internal__[31]), so pj_ioqueue_op_key_t size + * must cover all above. + */ + PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-3*sizeof(void*) >= sizeof(union operation_key), PJ_EBUG); + if (!fnCancelIoEx) { + fnCancelIoEx = (FnCancelIoEx) + GetProcAddress(GetModuleHandle(PJ_T("Kernel32.dll")), + "CancelIoEx"); + if (!fnCancelIoEx) { + rc = PJ_RETURN_OS_ERROR(GetLastError()); + PJ_PERROR(1, (THIS_FILE, rc, + "Failed in getting address of CancelIoEx()")); + return rc; + } + } + /* Create IOCP */ ioqueue = pj_pool_zalloc(pool, sizeof(*ioqueue)); + ioqueue->pool = pool; if (cfg) pj_memcpy(&ioqueue->cfg, cfg, sizeof(*cfg)); else @@ -402,13 +464,11 @@ PJ_DEF(pj_status_t) pj_ioqueue_create2(pj_pool_t *pool, ioqueue->auto_delete_lock = PJ_TRUE; ioqueue->default_concurrency = PJ_IOQUEUE_DEFAULT_ALLOW_CONCURRENCY; -#if PJ_IOQUEUE_HAS_SAFE_UNREG /* * Create and initialize key pools. */ pj_list_init(&ioqueue->active_list); pj_list_init(&ioqueue->free_list); - pj_list_init(&ioqueue->closing_list); /* Preallocate keys according to max_fd setting, and put them * in free_list. @@ -416,40 +476,19 @@ PJ_DEF(pj_status_t) pj_ioqueue_create2(pj_pool_t *pool, for (i=0; iref_count); - if (rc != PJ_SUCCESS) { - key = ioqueue->free_list.next; - while (key != &ioqueue->free_list) { - pj_atomic_destroy(key->ref_count); - pj_mutex_destroy(key->mutex); - key = key->next; - } - CloseHandle(ioqueue->iocp); - return rc; - } + key = pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); - rc = pj_mutex_create_recursive(pool, "ioqkey", &key->mutex); - if (rc != PJ_SUCCESS) { - pj_atomic_destroy(key->ref_count); - key = ioqueue->free_list.next; - while (key != &ioqueue->free_list) { - pj_atomic_destroy(key->ref_count); - pj_mutex_destroy(key->mutex); - key = key->next; - } - CloseHandle(ioqueue->iocp); - return rc; - } + /* Initialize pending op lists */ + pj_list_init(&key->pending_list); + pj_list_init(&key->free_pending_list); pj_list_push_back(&ioqueue->free_list, key); } -#endif + ioqueue->max_fd = max_fd; *p_ioqueue = ioqueue; - PJ_LOG(4, ("pjlib", "WinNT IOCP I/O Queue created (%p)", ioqueue)); + PJ_LOG(4, (THIS_FILE, "WinNT IOCP I/O Queue created (%p)", ioqueue)); return PJ_SUCCESS; } @@ -461,7 +500,8 @@ PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioqueue ) #if PJ_HAS_TCP unsigned i; #endif - pj_ioqueue_key_t *key; + pj_ioqueue_key_t *key, *next; + pj_time_val stop; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL); @@ -476,34 +516,43 @@ PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioqueue ) ioqueue->event_count = 0; #endif - if (CloseHandle(ioqueue->iocp) != TRUE) - return PJ_RETURN_OS_ERROR(GetLastError()); - -#if PJ_IOQUEUE_HAS_SAFE_UNREG - /* Destroy reference counters */ + /* Destroy active keys */ key = ioqueue->active_list.next; while (key != &ioqueue->active_list) { - pj_atomic_destroy(key->ref_count); - pj_mutex_destroy(key->mutex); - key = key->next; + next = key->next; + pj_ioqueue_unregister(key); + key = next; } - key = ioqueue->closing_list.next; - while (key != &ioqueue->closing_list) { - pj_atomic_destroy(key->ref_count); - pj_mutex_destroy(key->mutex); - key = key->next; - } + pj_lock_release(ioqueue->lock); - key = ioqueue->free_list.next; - while (key != &ioqueue->free_list) { - pj_atomic_destroy(key->ref_count); - pj_mutex_destroy(key->mutex); - key = key->next; + /* Wait cancelling pending ops. */ + pj_gettickcount(&stop); + stop.msec += TIMEOUT_CANCEL_OP; + pj_time_val_normalize(&stop); + + while (1) { + pj_time_val timeout = {0, 100}; + pj_size_t pending_key_cnt; + + pending_key_cnt = ioqueue->max_fd - pj_list_size(&ioqueue->free_list); + if (!pending_key_cnt) + break; + + pj_ioqueue_poll(ioqueue, &timeout); + + pj_gettickcount(&timeout); + if (PJ_TIME_VAL_GTE(timeout, stop)) { + PJ_LOG(3, (THIS_FILE, "Warning, IOCP destroy timeout in waiting " + "for cancelling ops, after %dms, pending keys=%d", + TIMEOUT_CANCEL_OP, (int)pending_key_cnt)); + break; + } } -#endif - pj_lock_release(ioqueue->lock); + if (CloseHandle(ioqueue->iocp) != TRUE) + return PJ_RETURN_OS_ERROR(GetLastError()); + if (ioqueue->auto_delete_lock) pj_lock_destroy(ioqueue->lock); @@ -559,34 +608,32 @@ PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool, pj_lock_acquire(ioqueue->lock); -#if PJ_IOQUEUE_HAS_SAFE_UNREG - /* Scan closing list first to release unused keys. - * Must do this with lock acquired. - */ - scan_closing_keys(ioqueue); - - /* If safe unregistration is used, then get the key record from - * the free list. - */ - pj_assert(!pj_list_empty(&ioqueue->free_list)); + /* Verify that there is a free key */ if (pj_list_empty(&ioqueue->free_list)) { pj_lock_release(ioqueue->lock); return PJ_ETOOMANY; } + /* Get the key record from the free list. */ rec = ioqueue->free_list.next; - pj_list_erase(rec); - /* Set initial reference count to 1 */ - pj_assert(pj_atomic_get(rec->ref_count) == 0); - pj_atomic_inc(rec->ref_count); + /* Create pool for this key */ + rec->pool = pj_pool_create(ioqueue->pool->factory, "key%p", + 512, 512, NULL); + if (!rec->pool) { + pj_lock_release(ioqueue->lock); + return PJ_ENOMEM; + } - rec->closing = 0; + /* Move key from free list to active list */ + pj_list_erase(rec); + pj_list_push_back(&ioqueue->active_list, rec); -#else - rec = (pj_ioqueue_key_t *)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); -#endif + pj_lock_release(ioqueue->lock); + + rec->closing = 0; + /* Build the key for this socket. */ rec->ioqueue = ioqueue; rec->hnd = (HANDLE)sock; @@ -596,10 +643,8 @@ PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool, /* Set concurrency for this handle */ rc = pj_ioqueue_set_concurrency(rec, ioqueue->default_concurrency); - if (rc != PJ_SUCCESS) { - pj_lock_release(ioqueue->lock); + if (rc != PJ_SUCCESS) return rc; - } #if PJ_HAS_TCP rec->connecting = 0; @@ -608,36 +653,34 @@ PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool, /* Set socket to nonblocking. */ value = 1; rc = ioctlsocket(sock, FIONBIO, &value); - if (rc != 0) { - pj_lock_release(ioqueue->lock); + if (rc != 0) return PJ_RETURN_OS_ERROR(WSAGetLastError()); - } /* Associate with IOCP */ - hioq = CreateIoCompletionPort((HANDLE)sock, ioqueue->iocp, (ULONG_PTR)rec, 0); - if (!hioq) { - pj_lock_release(ioqueue->lock); + hioq = CreateIoCompletionPort((HANDLE)sock, ioqueue->iocp, + (ULONG_PTR)rec, 0); + if (!hioq) return PJ_RETURN_OS_ERROR(GetLastError()); - } - /* Group lock */ - rec->grp_lock = grp_lock; - if (rec->grp_lock) { - /* IOCP backend doesn't have group lock functionality, so - * you should not use it other than for experimental purposes. - */ - PJ_TODO(INTEGRATE_GROUP_LOCK); - // pj_grp_lock_add_ref_dbg(rec->grp_lock, "ioqueue", 0); + /* Create group lock if not specified */ + if (!grp_lock) { + pj_status_t status; + status = pj_grp_lock_create_w_handler(rec->pool, NULL, rec, + &key_on_destroy, &grp_lock); + if (status != PJ_SUCCESS) { + key_on_destroy(rec); + return status; + } } + rec->grp_lock = grp_lock; - *key = rec; - -#if PJ_IOQUEUE_HAS_SAFE_UNREG - pj_list_push_back(&ioqueue->active_list, rec); -#endif + /* Set initial reference count to 1 */ + increment_counter(rec); - pj_lock_release(ioqueue->lock); + TRACE((THIS_FILE, "REG key %p", rec)); + /* Finally */ + *key = rec; return PJ_SUCCESS; } @@ -683,28 +726,116 @@ PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key, } -#if PJ_IOQUEUE_HAS_SAFE_UNREG +static void key_on_destroy(void *data) { + pj_ioqueue_key_t *key = (pj_ioqueue_key_t*)data; + pj_ioqueue_t* ioqueue = key->ioqueue; + + /* Reset pool & keys */ + key->grp_lock = NULL; + pj_pool_safe_release(&key->pool); + + /* Reset free pending lists */ + pj_assert(pj_list_empty(&key->pending_list)); + pj_list_init(&key->free_pending_list); + + /* Return key to free list */ + pj_lock_acquire(ioqueue->lock); + pj_list_erase(key); + pj_list_push_back(&ioqueue->free_list, key); + + TRACE((THIS_FILE, "FREE key %p", key)); + + pj_lock_release(ioqueue->lock); +} + + +/* Increment the key's reference counter. */ +static void increment_counter(pj_ioqueue_key_t* key) +{ + pj_grp_lock_add_ref_dbg(key->grp_lock, "ioqueue", 0); +} + + /* Decrement the key's reference counter, and when the counter reach zero, * destroy the key. */ static void decrement_counter(pj_ioqueue_key_t *key) { - if (pj_atomic_dec_and_get(key->ref_count) == 0) { + pj_grp_lock_dec_ref_dbg(key->grp_lock, "ioqueue", 0); +} + +static struct pending_op *alloc_pending_op(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + void *buf, + pj_ssize_t len) +{ + struct pending_op *op = NULL; + int ref_cnt; + + pj_assert(key && op_key); + + /* Get pending op from free op list, or create a new one if none */ + pj_ioqueue_lock_key(key); + ref_cnt = pj_grp_lock_get_ref(key->grp_lock); + + if (pj_list_empty(&key->free_pending_list)) { + op = PJ_POOL_ZALLOC_T(key->pool, struct pending_op); + if (!op) { + pj_ioqueue_unlock_key(key); + return NULL; + } + pj_list_init(op); + } else { + op = key->free_pending_list.next; + pj_list_erase(op); + } + pj_list_push_back(&key->pending_list, op); + increment_counter(key); + pj_ioqueue_unlock_key(key); - pj_lock_acquire(key->ioqueue->lock); + /* Init the pending op */ + op->app_op_key = op_key; + op->pending_key.overlapped.wsabuf.buf = (CHAR*)buf; + op->pending_key.overlapped.wsabuf.len = (ULONG)len; - pj_assert(key->closing == 1); - pj_gettickcount(&key->free_time); - key->free_time.msec += PJ_IOQUEUE_KEY_FREE_DELAY; - pj_time_val_normalize(&key->free_time); + /* Link app op key to pending-op */ + op_key->internal__[PENDING_OP_POS(op_key)] = op; - pj_list_erase(key); - pj_list_push_back(&key->ioqueue->closing_list, key); + TRACE((THIS_FILE, "ALLOC op key %p (cnt=%d) op %p", key, ref_cnt, op)); + + return op; +} - pj_lock_release(key->ioqueue->lock); +static void release_pending_op(pj_ioqueue_key_t *key, struct pending_op *op) +{ + int ref_cnt; + + pj_assert(key && op); + pj_ioqueue_lock_key(key); + pj_list_erase(op); + pj_list_push_back(&key->free_pending_list, op); + decrement_counter(key); + ref_cnt = pj_grp_lock_get_ref(key->grp_lock); + pj_ioqueue_unlock_key(key); + + TRACE((THIS_FILE, "RELEASE op key %p (cnt=%d) op %p", key, ref_cnt, op)); +} + +static pj_status_t cancel_all_pending_op(pj_ioqueue_key_t *key) +{ + BOOL rc = fnCancelIoEx(key->hnd, NULL); + + if (rc == 0) { + DWORD dwError = WSAGetLastError(); + if (dwError != ERROR_NOT_FOUND) { + TRACE((THIS_FILE, "CANCEL key %p error %d", key, dwError)); + return PJ_RETURN_OS_ERROR(dwError); + } } + + TRACE((THIS_FILE, "CANCEL key %p success", key)); + return PJ_SUCCESS; } -#endif /* * Poll the I/O Completion Port, execute callback, @@ -719,11 +850,18 @@ static pj_bool_t poll_iocp( HANDLE hIocp, DWORD dwTimeout, pj_ioqueue_key_t *key; pj_ssize_t size_status = -1; BOOL rcGetQueued; + struct pending_op *op = NULL; + pj_ioqueue_op_key_t *op_key = NULL; /* Poll for completion status. */ rcGetQueued = GetQueuedCompletionStatus(hIocp, &dwBytesTransferred, &dwKey, (OVERLAPPED**)&pOv, dwTimeout); + if (!rcGetQueued && pOv) { + PJ_PERROR(4, (THIS_FILE, PJ_STATUS_FROM_OS(GetLastError()), + "GetQueuedCompletionStatus() error dwKey:%p, pOv:%p", + (void *)dwKey, pOv)); + } /* The return value is: * - nonzero if event was dequeued. @@ -732,6 +870,7 @@ static pj_bool_t poll_iocp( HANDLE hIocp, DWORD dwTimeout, */ if (pOv) { pj_bool_t has_lock; + pj_ioqueue_operation_e operation = pOv->operation; /* Event was dequeued for either successfull or failed I/O */ key = (pj_ioqueue_key_t*)dwKey; @@ -743,16 +882,37 @@ static pj_bool_t poll_iocp( HANDLE hIocp, DWORD dwTimeout, if (p_key) *p_key = key; -#if PJ_IOQUEUE_HAS_SAFE_UNREG + switch(operation) + { + case PJ_IOQUEUE_OP_RECV: + case PJ_IOQUEUE_OP_RECV_FROM: + case PJ_IOQUEUE_OP_SEND: + case PJ_IOQUEUE_OP_SEND_TO: + case PJ_IOQUEUE_OP_ACCEPT: + op = (struct pending_op*) + ((char*)pOv - offsetof(struct pending_op, pending_key)); + op_key = op->app_op_key; + break; + default: + /* Invalid operation, just release op & ignore */ + pj_assert(0); + op = (struct pending_op*) + ((char*)pOv - offsetof(struct pending_op, pending_key)); + release_pending_op(key, op); + return PJ_TRUE; + } + /* We shouldn't call callbacks if key is quitting. */ - if (key->closing) + if (key->closing) { + release_pending_op(key, op); return PJ_TRUE; + } /* If concurrency is disabled, lock the key * (and save the lock status to local var since app may change * concurrency setting while in the callback) */ if (key->allow_concurrent == PJ_FALSE) { - pj_mutex_lock(key->mutex); + pj_ioqueue_lock_key(key); has_lock = PJ_TRUE; } else { has_lock = PJ_FALSE; @@ -761,40 +921,39 @@ static pj_bool_t poll_iocp( HANDLE hIocp, DWORD dwTimeout, /* Now that we get the lock, check again that key is not closing */ if (key->closing) { if (has_lock) { - pj_mutex_unlock(key->mutex); + pj_ioqueue_unlock_key(key); } + release_pending_op(key, op); return PJ_TRUE; } /* Increment reference counter to prevent this key from being * deleted */ - pj_atomic_inc(key->ref_count); -#else - PJ_UNUSED_ARG(has_lock); -#endif + increment_counter(key); /* Carry out the callback */ - switch (pOv->operation) { + switch (operation) { case PJ_IOQUEUE_OP_READ: case PJ_IOQUEUE_OP_RECV: case PJ_IOQUEUE_OP_RECV_FROM: - pOv->operation = 0; + //pOv->operation = 0; + OPKEY_OPERATION(op_key) = 0; if (key->cb.on_read_complete) - key->cb.on_read_complete(key, (pj_ioqueue_op_key_t*)pOv, - size_status); + key->cb.on_read_complete(key, op_key, size_status); break; case PJ_IOQUEUE_OP_WRITE: case PJ_IOQUEUE_OP_SEND: case PJ_IOQUEUE_OP_SEND_TO: - pOv->operation = 0; + //pOv->operation = 0; + OPKEY_OPERATION(op_key) = 0; if (key->cb.on_write_complete) - key->cb.on_write_complete(key, (pj_ioqueue_op_key_t*)pOv, - size_status); + key->cb.on_write_complete(key, op_key, size_status); break; #if PJ_HAS_TCP case PJ_IOQUEUE_OP_ACCEPT: /* special case for accept. */ + OPKEY_OPERATION(op_key) = 0; ioqueue_on_accept_complete(key, (ioqueue_accept_rec*)pOv); if (key->cb.on_accept_complete) { ioqueue_accept_rec *accept_rec = (ioqueue_accept_rec*)pOv; @@ -810,9 +969,7 @@ static pj_bool_t poll_iocp( HANDLE hIocp, DWORD dwTimeout, status = PJ_RETURN_OS_ERROR(dwError); } - key->cb.on_accept_complete(key, (pj_ioqueue_op_key_t*)pOv, - newsock, status); - + key->cb.on_accept_complete(key, op_key, newsock, status); } break; case PJ_IOQUEUE_OP_CONNECT: @@ -822,11 +979,11 @@ static pj_bool_t poll_iocp( HANDLE hIocp, DWORD dwTimeout, break; } -#if PJ_IOQUEUE_HAS_SAFE_UNREG - decrement_counter(key); if (has_lock) - pj_mutex_unlock(key->mutex); -#endif + pj_ioqueue_unlock_key(key); + + release_pending_op(key, op); + decrement_counter(key); return PJ_TRUE; } @@ -840,12 +997,16 @@ static pj_bool_t poll_iocp( HANDLE hIocp, DWORD dwTimeout, */ PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key ) { - unsigned i; - pj_bool_t has_lock; + //unsigned i; + //pj_bool_t has_lock; enum { RETRY = 10 }; PJ_ASSERT_RETURN(key, PJ_EINVAL); + /* Best effort to avoid double key-unregistration */ + if (!key->grp_lock || key->closing) + return PJ_SUCCESS; + #if PJ_HAS_TCP if (key->connecting) { unsigned pos; @@ -866,23 +1027,22 @@ PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key ) } #endif -#if PJ_IOQUEUE_HAS_SAFE_UNREG /* Mark key as closing before closing handle. */ key->closing = 1; /* If concurrency is disabled, wait until the key has finished * processing the callback */ - if (key->allow_concurrent == PJ_FALSE) { - pj_mutex_lock(key->mutex); - has_lock = PJ_TRUE; - } else { - has_lock = PJ_FALSE; - } -#else - PJ_UNUSED_ARG(has_lock); -#endif - + //if (key->allow_concurrent == PJ_FALSE) { + // pj_ioqueue_lock_key(key); + // has_lock = PJ_TRUE; + //} else { + // has_lock = PJ_FALSE; + //} + + /* Cancel all pending I/O operations (asynchronously) */ + cancel_all_pending_op(key); + /* Close handle (the only way to disassociate handle from IOCP). * We also need to close handle to make sure that no further events * will come to the handle. @@ -909,7 +1069,6 @@ PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key ) key->cb.on_read_complete = NULL; key->cb.on_write_complete = NULL; -#if PJ_IOQUEUE_HAS_SAFE_UNREG /* Even after handle is closed, I suspect that IOCP may still try to * do something with the handle, causing memory corruption when pool * debugging is enabled. @@ -921,55 +1080,34 @@ PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key ) * This should not happen if concurrency is disallowed for the key. * So at least application has a solution for this (i.e. by disallowing * concurrency in the key). + * + * Update 2025/01/20: + * Any pending ops will be cancelled asynchronously, so key resources + * will be released later from the group lock handler after all + * pending ops are cancelled. */ //This will loop forever if unregistration is done on the callback. //Doing this with RETRY I think should solve the IOCP setting the //socket signalled, without causing the deadlock. //while (pj_atomic_get(key->ref_count) != 1) // pj_thread_sleep(0); - for (i=0; pj_atomic_get(key->ref_count) != 1 && iref_count) != 1 && imutex); -#endif + TRACE((THIS_FILE, "UNREG key %p ref cnt %d", + key, pj_grp_lock_get_ref(key->grp_lock))); - return PJ_SUCCESS; -} - -#if PJ_IOQUEUE_HAS_SAFE_UNREG -/* Scan the closing list, and put pending closing keys to free list. - * Must do this with ioqueue mutex held. - */ -static void scan_closing_keys(pj_ioqueue_t *ioqueue) -{ - if (!pj_list_empty(&ioqueue->closing_list)) { - pj_time_val now; - pj_ioqueue_key_t *key; - - pj_gettickcount(&now); - - /* Move closing keys to free list when they've finished the closing - * idle time. - */ - key = ioqueue->closing_list.next; - while (key != &ioqueue->closing_list) { - pj_ioqueue_key_t *next = key->next; - - pj_assert(key->closing != 0); + /* Decrement reference counter to destroy the key. + * If the key has pending op, it will be destroyed only after the op is + * cancelled (asynchronously). + */ + decrement_counter(key); - if (PJ_TIME_VAL_GTE(now, key->free_time)) { - pj_list_erase(key); - pj_list_push_back(&ioqueue->free_list, key); - } - key = next; - } - } + return PJ_SUCCESS; } -#endif /* * pj_ioqueue_poll() @@ -1001,17 +1139,6 @@ PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout) } #endif -#if PJ_IOQUEUE_HAS_SAFE_UNREG - /* Check the closing keys only when there's no activity and when there are - * pending closing keys. - */ - if (event_count == 0 && !pj_list_empty(&ioqueue->closing_list)) { - pj_lock_acquire(ioqueue->lock); - scan_closing_keys(ioqueue); - pj_lock_release(ioqueue->lock); - } -#endif - /* Return number of events. */ return event_count; } @@ -1036,19 +1163,18 @@ PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key, DWORD bytesRead; DWORD dwFlags = 0; union operation_key *op_key_rec; + struct pending_op *op; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL); -#if PJ_IOQUEUE_HAS_SAFE_UNREG /* Check key is not closing */ if (key->closing) return PJ_ECANCELLED; -#endif op_key_rec = (union operation_key*)op_key->internal__; op_key_rec->overlapped.wsabuf.buf = buffer; - op_key_rec->overlapped.wsabuf.len = *length; + op_key_rec->overlapped.wsabuf.len = (ULONG)*length; dwFlags = flags; @@ -1070,6 +1196,12 @@ PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key, } } + op = alloc_pending_op(key, op_key, buffer, *length); + if (!op) + return PJ_ENOMEM; + + op_key_rec = &op->pending_key; + dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); /* @@ -1079,6 +1211,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key, pj_bzero( &op_key_rec->overlapped.overlapped, sizeof(op_key_rec->overlapped.overlapped)); op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_RECV; + OPKEY_OPERATION(op_key) = PJ_IOQUEUE_OP_RECV; rc = WSARecv((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, &bytesRead, &dwFlags, @@ -1087,6 +1220,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key, DWORD dwStatus = WSAGetLastError(); if (dwStatus!=WSA_IO_PENDING) { *length = -1; + release_pending_op(key, op); return PJ_STATUS_FROM_OS(dwStatus); } } @@ -1112,19 +1246,18 @@ PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key, DWORD bytesRead; DWORD dwFlags = 0; union operation_key *op_key_rec; + struct pending_op *op; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(key && op_key && buffer, PJ_EINVAL); -#if PJ_IOQUEUE_HAS_SAFE_UNREG /* Check key is not closing */ if (key->closing) return PJ_ECANCELLED; -#endif op_key_rec = (union operation_key*)op_key->internal__; op_key_rec->overlapped.wsabuf.buf = buffer; - op_key_rec->overlapped.wsabuf.len = *length; + op_key_rec->overlapped.wsabuf.len = (ULONG)*length; dwFlags = flags; @@ -1146,6 +1279,12 @@ PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key, } } + op = alloc_pending_op(key, op_key, buffer, *length); + if (!op) + return PJ_ENOMEM; + + op_key_rec = &op->pending_key; + dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); /* @@ -1155,6 +1294,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key, pj_bzero( &op_key_rec->overlapped.overlapped, sizeof(op_key_rec->overlapped.overlapped)); op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_RECV; + OPKEY_OPERATION(op_key) = PJ_IOQUEUE_OP_RECV; rc = WSARecvFrom((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, &bytesRead, &dwFlags, addr, addrlen, @@ -1163,10 +1303,11 @@ PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key, DWORD dwStatus = WSAGetLastError(); if (dwStatus!=WSA_IO_PENDING) { *length = -1; + release_pending_op(key, op); return PJ_STATUS_FROM_OS(dwStatus); } } - + /* Pending operation has been scheduled. */ return PJ_EPENDING; } @@ -1203,15 +1344,14 @@ PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key, DWORD bytesWritten; DWORD dwFlags; union operation_key *op_key_rec; + struct pending_op *op; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(key && op_key && data, PJ_EINVAL); -#if PJ_IOQUEUE_HAS_SAFE_UNREG /* Check key is not closing */ if (key->closing) return PJ_ECANCELLED; -#endif op_key_rec = (union operation_key*)op_key->internal__; @@ -1219,7 +1359,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key, * First try blocking write. */ op_key_rec->overlapped.wsabuf.buf = (void*)data; - op_key_rec->overlapped.wsabuf.len = *length; + op_key_rec->overlapped.wsabuf.len = (ULONG)*length; dwFlags = flags; @@ -1239,6 +1379,12 @@ PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key, } } + op = alloc_pending_op(key, op_key, (void *)data, *length); + if (!op) + return PJ_ENOMEM; + + op_key_rec = &op->pending_key; + dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); /* @@ -1248,14 +1394,17 @@ PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key, pj_bzero( &op_key_rec->overlapped.overlapped, sizeof(op_key_rec->overlapped.overlapped)); op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_SEND; + OPKEY_OPERATION(op_key) = PJ_IOQUEUE_OP_SEND; rc = WSASendTo((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, &bytesWritten, dwFlags, addr, addrlen, &op_key_rec->overlapped.overlapped, NULL); if (rc == SOCKET_ERROR) { DWORD dwStatus = WSAGetLastError(); - if (dwStatus!=WSA_IO_PENDING) + if (dwStatus!=WSA_IO_PENDING) { + release_pending_op(key, op); return PJ_STATUS_FROM_OS(dwStatus); + } } /* Asynchronous operation successfully submitted. */ @@ -1281,15 +1430,14 @@ PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key, pj_status_t status; union operation_key *op_key_rec; SOCKET sock; + struct pending_op *op; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL); -#if PJ_IOQUEUE_HAS_SAFE_UNREG /* Check key is not closing */ if (key->closing) return PJ_ECANCELLED; -#endif /* * See if there is a new connection immediately available. @@ -1331,13 +1479,20 @@ PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key, * No connection is immediately available. * Must schedule an asynchronous operation. */ - op_key_rec = (union operation_key*)op_key->internal__; - + op = alloc_pending_op(key, op_key, NULL, 0); + if (!op) + return PJ_ENOMEM; + + op_key_rec = &op->pending_key; + status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &op_key_rec->accept.newsock); - if (status != PJ_SUCCESS) + if (status != PJ_SUCCESS) { + release_pending_op(key, op); return status; + } + OPKEY_OPERATION(op_key) = PJ_IOQUEUE_OP_ACCEPT; op_key_rec->accept.operation = PJ_IOQUEUE_OP_ACCEPT; op_key_rec->accept.addrlen = addrlen; op_key_rec->accept.local = local; @@ -1354,11 +1509,14 @@ PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key, if (rc == TRUE) { ioqueue_on_accept_complete(key, &op_key_rec->accept); + release_pending_op(key, op); return PJ_SUCCESS; } else { DWORD dwStatus = WSAGetLastError(); - if (dwStatus!=WSA_IO_PENDING) + if (dwStatus!=WSA_IO_PENDING) { + release_pending_op(key, op); return PJ_STATUS_FROM_OS(dwStatus); + } } /* Asynchronous Accept() has been submitted. */ @@ -1382,11 +1540,9 @@ PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key, PJ_CHECK_STACK(); PJ_ASSERT_RETURN(key && addr && addrlen, PJ_EINVAL); -#if PJ_IOQUEUE_HAS_SAFE_UNREG /* Check key is not closing */ if (key->closing) return PJ_ECANCELLED; -#endif /* Initiate connect() */ if (connect((pj_sock_t)key->hnd, addr, addrlen) != 0) { @@ -1456,10 +1612,26 @@ PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key, PJ_DEF(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key ) { + struct generic_overlapped* op_rec; + + PJ_UNUSED_ARG(key); + + /* Instead of using GetOverlappedResult(), simply checking the operation + * status should be fine. + */ + op_rec = (struct generic_overlapped*)op_key; + return op_rec->operation != 0; + +#if 0 BOOL rc; DWORD bytesTransferred; + struct pending_op *op; + + op = get_pending_op(op_key); + if (!op) + return PJ_FALSE; - rc = GetOverlappedResult( key->hnd, (LPOVERLAPPED)op_key, + rc = GetOverlappedResult( key->hnd, (LPOVERLAPPED)&op->pending_key, &bytesTransferred, FALSE ); if (rc == FALSE) { @@ -1467,6 +1639,7 @@ PJ_DEF(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key, } return FALSE; +#endif } @@ -1475,9 +1648,15 @@ PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key, pj_ssize_t bytes_status ) { BOOL rc; + struct pending_op* op; + + op = get_pending_op(op_key); + if (!op) + return PJ_EINVAL; - rc = PostQueuedCompletionStatus(key->ioqueue->iocp, bytes_status, - (ULONG_PTR)key, (OVERLAPPED*)op_key ); + rc = PostQueuedCompletionStatus(key->ioqueue->iocp, (DWORD)bytes_status, + (ULONG_PTR)key, + (OVERLAPPED*)&op->pending_key ); if (rc == FALSE) { return PJ_RETURN_OS_ERROR(GetLastError()); } @@ -1501,20 +1680,14 @@ PJ_DEF(pj_status_t) pj_ioqueue_set_concurrency(pj_ioqueue_key_t *key, PJ_DEF(pj_status_t) pj_ioqueue_lock_key(pj_ioqueue_key_t *key) { -#if PJ_IOQUEUE_HAS_SAFE_UNREG - return pj_mutex_lock(key->mutex); -#else - PJ_ASSERT_RETURN(!"PJ_IOQUEUE_HAS_SAFE_UNREG is disabled", PJ_EINVALIDOP); -#endif + PJ_ASSERT_RETURN(key && key->grp_lock, PJ_EINVAL); + return pj_grp_lock_acquire(key->grp_lock); } PJ_DEF(pj_status_t) pj_ioqueue_unlock_key(pj_ioqueue_key_t *key) { -#if PJ_IOQUEUE_HAS_SAFE_UNREG - return pj_mutex_unlock(key->mutex); -#else - PJ_ASSERT_RETURN(!"PJ_IOQUEUE_HAS_SAFE_UNREG is disabled", PJ_EINVALIDOP); -#endif + PJ_ASSERT_RETURN(key && key->grp_lock, PJ_EINVAL); + return pj_grp_lock_release(key->grp_lock); } PJ_DEF(pj_oshandle_t) pj_ioqueue_get_os_handle( pj_ioqueue_t *ioqueue ) diff --git a/pjlib/src/pjlib-test/ioq_iocp_unreg_test.c b/pjlib/src/pjlib-test/ioq_iocp_unreg_test.c new file mode 100644 index 0000000000..8371e0e78c --- /dev/null +++ b/pjlib/src/pjlib-test/ioq_iocp_unreg_test.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2024 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2024 jimying at github dot com. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + + /* + * IOCP crash reproduce test (issue #985). + * The code is taken from PR #4172 with few minor changes. + * Issue fix attempt is done in #4136. + * + * Note: + * - The crash was reproducible on Windows 10 & MSVC2005 (Win32), + * but not reproducible on Windows 11 & MSVC2022 (Win32 &x64). + * - Test can be run for any ioqueue and normally take less than one second. + */ +#include +#include "test.h" + +#define THIS_FILE "iocp_unregister_test.c" + +#define CLIENT_NUM (PJ_IOQUEUE_MAX_HANDLES-1) + +/** + * socket info + * has an independent memory pool, which is the key to successful + * reproduce crash + */ +struct sock_info_t { + pj_pool_t *pool; + pj_activesock_t *asock; + pj_sockaddr bound_addr; +}; + +struct iocp_test_t { + pj_pool_t *pool; + pj_ioqueue_t *ioq; + pj_thread_t *tid; + pj_bool_t quit; + struct sock_info_t *socks[CLIENT_NUM]; + pj_activesock_t *asock_send; +}; + + +static int worker_thread(void *p) +{ + struct iocp_test_t *test = (struct iocp_test_t *)p; + + while(!test->quit) { + pj_time_val timeout = {0, 100}; + pj_ioqueue_poll(test->ioq, &timeout); + } + + return 0; +} + +static unsigned recv_cnt; + +static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, + void *data, + pj_size_t size, + const pj_sockaddr_t *src_addr, + int addr_len, + pj_status_t status) +{ + (void)asock; + (void)src_addr; + (void)addr_len; + PJ_LOG(3, (THIS_FILE, "on_data_recvfrom() data:%.*s, status:%d", + (int)size, (char *)data, status)); + ++recv_cnt; + return PJ_TRUE; +} + +int iocp_unregister_test(void) +{ + struct iocp_test_t *test; + pj_pool_t *pool; + pj_status_t status; + unsigned i; + pj_activesock_cfg cfg; + pj_activesock_cb cb; + struct sock_info_t *sock_info; + pj_sockaddr loc_addr; + pj_str_t loop = {"127.0.0.1", 9}; + + // Let's just do it for any ioqueue. + //if (strcmp(pj_ioqueue_name(), "iocp")) { + // /* skip if ioqueue framework is not iocp */ + // return PJ_SUCCESS; + //} + + pool = pj_pool_create(mem, "iocp-crash-test", 500, 500, NULL); + test = PJ_POOL_ZALLOC_T(pool, struct iocp_test_t); + test->pool = pool; + status = pj_ioqueue_create(pool, CLIENT_NUM+1, &test->ioq); + if (status != PJ_SUCCESS) { + status = -900; + goto on_error; + } + + status = pj_thread_create(pool, "iocp-crash-test", worker_thread, + test, 0, 0, &test->tid); + if (status != PJ_SUCCESS) { + status = -901; + goto on_error; + } + + pj_activesock_cfg_default(&cfg); + pj_bzero(&cb, sizeof(cb)); + cb.on_data_recvfrom = on_data_recvfrom; + + /* create send socket */ + status = pj_activesock_create_udp(pool, NULL, &cfg, test->ioq, &cb, NULL, + &test->asock_send, NULL); + if (status != PJ_SUCCESS) { + status = -902; + goto on_error; + } + + /* create sockets to receive */ + pj_sockaddr_init(pj_AF_INET(), &loc_addr, &loop, 0); + for (i = 0; i < PJ_ARRAY_SIZE(test->socks); i++) { + pool = pj_pool_create(mem, "sock%p", 500, 500, NULL); + sock_info = PJ_POOL_ZALLOC_T(pool, struct sock_info_t); + sock_info->pool = pool; + + status = pj_activesock_create_udp(pool, &loc_addr, &cfg, test->ioq, + &cb, NULL, &sock_info->asock, + &sock_info->bound_addr); + if (status != PJ_SUCCESS) { + status = -903; + pj_pool_release(pool); + goto on_error; + } + test->socks[i] = sock_info; + pj_activesock_start_recvfrom(sock_info->asock, pool, 256, 0); + } + + /* send 'hello' to every socks */ + for (i = 0; i < PJ_ARRAY_SIZE(test->socks); i++) { + pj_ioqueue_op_key_t *send_key; + pj_str_t data; + pj_ssize_t sent; + + sock_info = test->socks[i]; + send_key = PJ_POOL_ZALLOC_T(test->pool, pj_ioqueue_op_key_t); + pj_strdup2_with_null(test->pool, &data, "hello"); + sent = data.slen; + status = pj_activesock_sendto(test->asock_send, send_key, data.ptr, + &sent, 0, &sock_info->bound_addr, + pj_sockaddr_get_len(&sock_info->bound_addr)); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + char buf[80]; + pj_sockaddr_print(&sock_info->bound_addr, buf, sizeof(buf), 3); + PJ_PERROR(2, (THIS_FILE, status, "send error, dest:%s", buf)); + } + } + + pj_thread_sleep(20); + + /* close all socks */ + for (i = 0; i < PJ_ARRAY_SIZE(test->socks); i++) { + sock_info = test->socks[i]; + pj_activesock_close(sock_info->asock); + pj_pool_release(sock_info->pool); + test->socks[i] = NULL; + } + + pj_thread_sleep(20); + + /* quit */ + test->quit = PJ_TRUE; + status = PJ_SUCCESS; + +on_error: + if (test->tid) + pj_thread_join(test->tid); + for (i = 0; i < PJ_ARRAY_SIZE(test->socks); i++) { + sock_info = test->socks[i]; + if (!sock_info) + break; + pj_activesock_close(sock_info->asock); + pj_pool_release(sock_info->pool); + } + if(test->asock_send) + pj_activesock_close(test->asock_send); + if (test->ioq) + pj_ioqueue_destroy(test->ioq); + pj_pool_release(test->pool); + + PJ_LOG(3, (THIS_FILE, "Recv cnt = %u", recv_cnt)); + return status; +} diff --git a/pjlib/src/pjlib-test/ioq_stress_test.c b/pjlib/src/pjlib-test/ioq_stress_test.c index 61f22fc4e2..61783a9405 100644 --- a/pjlib/src/pjlib-test/ioq_stress_test.c +++ b/pjlib/src/pjlib-test/ioq_stress_test.c @@ -333,7 +333,7 @@ static void on_accept_complete(pj_ioqueue_key_t *key, status = pj_ioqueue_register_sock2(test->state.pool, test->state.ioq, test->state.socks[SERVER], - test->state.grp_lock, + NULL, //test->state.grp_lock, test, &test_cb, &test->state.keys[SERVER]); @@ -446,7 +446,8 @@ static int worker_thread(void *p) op_key_user_data *okud = &test->state.okuds[CLIENT][i]; pj_lock_acquire((pj_lock_t*)test->state.grp_lock); if (!pj_ioqueue_is_pending(test->state.keys[CLIENT], - &okud->client.send_op)) { + &okud->client.send_op)) + { on_write_complete(test->state.keys[CLIENT], &okud->client.send_op, -12345); } @@ -529,7 +530,7 @@ static int perform_single_pass(test_desc *test) CHECK(24, pj_ioqueue_register_sock2(test->state.pool, test->state.ioq, test->state.listen_sock, - test->state.grp_lock, + NULL, //test->state.grp_lock, test, &test_cb, &test->state.listen_key)); @@ -559,7 +560,7 @@ static int perform_single_pass(test_desc *test) CHECK(33, pj_ioqueue_register_sock2(test->state.pool, test->state.ioq, test->state.socks[SERVER], - test->state.grp_lock, + NULL, //test->state.grp_lock, test, &test_cb, &test->state.keys[SERVER])); @@ -604,10 +605,18 @@ static int perform_single_pass(test_desc *test) pj_SO_RCVBUF(), &value, sizeof(value))); } + + /* We cannot use the global group lock for registering keys (here and + * all below) because currently IOCP key uses the group lock handler for + * releasing its resources including the key itself. If the key is not + * released (the global group lock destroy is done very late) and + * as the ioqueue capacity for the tests are quite limited (~4-6 keys), + * ioqueue will get full quickly and tests will fail. + */ CHECK(42, pj_ioqueue_register_sock2(test->state.pool, test->state.ioq, test->state.socks[CLIENT], - test->state.grp_lock, + NULL, //test->state.grp_lock, test, &test_cb, &test->state.keys[CLIENT])); diff --git a/pjlib/src/pjlib-test/ioq_udp.c b/pjlib/src/pjlib-test/ioq_udp.c index cb038ffe42..ef06cc19f1 100644 --- a/pjlib/src/pjlib-test/ioq_udp.c +++ b/pjlib/src/pjlib-test/ioq_udp.c @@ -940,7 +940,6 @@ static int bench_test(const pj_ioqueue_cfg *cfg, int bufsize, pj_timestamp t1, t2, t_elapsed; int rc=0, i; /* i must be signed */ pj_str_t temp; - char errbuf[PJ_ERR_MSG_SIZE]; TRACE__((THIS_FILE, " bench test %d", inactive_sock_count)); @@ -1161,8 +1160,7 @@ static int bench_test(const pj_ioqueue_cfg *cfg, int bufsize, return rc; on_error: - pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf)); - PJ_LOG(1,(THIS_FILE, "...ERROR: %s", errbuf)); + PJ_PERROR(1,(THIS_FILE, pj_get_netos_error(), "...ERROR")); if (ssock >= 0) pj_sock_close(ssock); if (csock >= 0) diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c index ba1a9bbb3d..8bd8ba5b8e 100644 --- a/pjlib/src/pjlib-test/test.c +++ b/pjlib/src/pjlib-test/test.c @@ -344,6 +344,10 @@ static int features_tests(int argc, char *argv[]) UT_ADD_TEST(&test_app.ut_app, ssl_sock_test, 0); #endif +#if INCLUDE_IOCP_UNREG_TEST + UT_ADD_TEST(&test_app.ut_app, iocp_unregister_test, 0); +#endif + #undef ADD_TEST diff --git a/pjlib/src/pjlib-test/test.h b/pjlib/src/pjlib-test/test.h index 7a109a5fe2..1b892d88e6 100644 --- a/pjlib/src/pjlib-test/test.h +++ b/pjlib/src/pjlib-test/test.h @@ -65,6 +65,7 @@ #define INCLUDE_IOQUEUE_STRESS_TEST (PJ_HAS_THREADS && GROUP_NETWORK) #define INCLUDE_UDP_IOQUEUE_TEST GROUP_NETWORK #define INCLUDE_TCP_IOQUEUE_TEST GROUP_NETWORK +#define INCLUDE_IOCP_UNREG_TEST GROUP_NETWORK #define INCLUDE_ACTIVESOCK_TEST GROUP_NETWORK #define INCLUDE_SSLSOCK_TEST (PJ_HAS_SSL_SOCK && GROUP_NETWORK) #define INCLUDE_IOQUEUE_PERF_TEST (PJ_HAS_THREADS && GROUP_NETWORK && WITH_BENCHMARK) @@ -114,6 +115,7 @@ extern int tcp_ioqueue_test(void); extern int ioqueue_perf_test0(void); extern int ioqueue_perf_test1(void); extern int ioqueue_stress_test(void); +extern int iocp_unregister_test(void); extern int activesock_test(void); extern int file_test(void); extern int ssl_sock_test(void); From 3fcce513b9d21fa4ec03a6ab2f65ce8040dce820 Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 5 Feb 2025 11:43:27 +0800 Subject: [PATCH 187/491] Fixed OpenSSL log error reading cert (#4291) --- pjlib/src/pj/ssl_sock_ossl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c index ec01435bdc..ed1fde2761 100644 --- a/pjlib/src/pj/ssl_sock_ossl.c +++ b/pjlib/src/pj/ssl_sock_ossl.c @@ -1573,7 +1573,7 @@ static pj_status_t init_ossl_ctx(pj_ssl_sock_t *ssock) PJ_LOG(4,(ssock->pool->obj_name, "CA certificates loaded from %s", (cert->CA_file.slen?cert->CA_file.ptr:"buffer"))); - } else { + } else if (cert->CA_file.slen > 0 || cert->CA_buf.slen > 0) { PJ_LOG(1,(ssock->pool->obj_name, "Error reading CA certificates from %s", (cert->CA_file.slen?cert->CA_file.ptr:"buffer"))); From 0252152dec26b8f9247ab9ab03f08edd1d161a41 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Thu, 6 Feb 2025 11:09:20 +0700 Subject: [PATCH 188/491] Use cirunner to capture and analyze GitHub action CI crash (#4288) * Windows runner implementation * Set timeout * Remove initial implementation of ci-runner here (it is on separate repo now) * Remove crash handling (-n) in main.c of unit tests * Install cirunner to CI workflows * Adding crash to timestamp test * Fix missing cirunner in one of the job * Reinstall core_pattern on Linux * Removed intentional crash in timestamp_test() * Upload program and core dump on crash * Add crash code in uri_test.c * Removed injected crash in uri_test. Disable stdout/stderr buffering for unit tests * Minor: remove space left out by previous clean up --- .github/workflows/ci-linux.yml | 114 ++++++++++++++++++++++ .github/workflows/ci-mac.yml | 132 ++++++++++++++++++++++++++ .github/workflows/ci-win.yml | 128 +++++++++++++++++++++---- Makefile | 10 +- pjlib-util/src/pjlib-util-test/main.c | 51 ---------- pjlib/src/pjlib-test/main.c | 48 ---------- pjlib/src/pjlib-test/test_util.h | 8 ++ pjmedia/src/test/main.c | 35 ------- pjnath/src/pjnath-test/main.c | 48 ---------- pjsip/src/test/main.c | 34 ------- 10 files changed, 367 insertions(+), 241 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 4f1a7e99bc..4c720fee65 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -8,6 +8,8 @@ on: env: CI_ARGS: -w 3 --shuffle --stdout-buf 1 CI_MODE: --ci-mode + # Setting CI_RUNNER to empty should disable cirunner + CI_RUNNER: python ${{ github.workspace }}/cirunner/cirunner.py -t 3600 -o ${{ github.workspace }}/artifacts -- MAKE_FAST: make -j 3 jobs: default-build: @@ -16,6 +18,10 @@ jobs: name: Default / build only steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installlinux.sh - name: configure run: ./configure - name: make @@ -24,6 +30,12 @@ jobs: run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL - name: verify oepnssl is used run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep -E 'PJ_SSL_SOCK_IMP\s+:\s+1' + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts default-full-bundle-1: # full bundle: enable all codecs + AEC + DTLS @@ -31,6 +43,10 @@ jobs: name: Default / pjmedia,pjsua steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installlinux.sh - name: install dependencies run: sudo apt-get update && sudo apt-get install -y swig sip-tester libopencore-amrnb-dev - name: config site @@ -61,12 +77,22 @@ jobs: run: make pjmedia-test - name: pjsua-test run: make pjsua-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts default-full-bundle-2: runs-on: ubuntu-latest name: Default / pjlib,util,pjnath steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installlinux.sh - name: install dependencies run: sudo apt-get install -y libopencore-amrnb-dev - name: config site @@ -81,12 +107,22 @@ jobs: run: make pjlib-util-test - name: pjnath-test run: make pjnath-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts default-full-bundle-3: runs-on: ubuntu-latest name: Default / pjsip steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installlinux.sh - name: install dependencies run: sudo apt-get install -y libopencore-amrnb-dev - name: config site @@ -97,12 +133,22 @@ jobs: run: $MAKE_FAST - name: pjsip-test run: make pjsip-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts no-tls: runs-on: ubuntu-latest name: No SSL / pjlib,pjsip steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installlinux.sh - name: install dependencies run: sudo apt-get install -y swig - name: configure @@ -115,6 +161,12 @@ jobs: run: make pjlib-test - name: pjsip-test run: make pjsip-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts # build-ubuntu-openssl # TLS: with OpenSSL (same as build-ubuntu-default) @@ -124,6 +176,10 @@ jobs: name: GnuTLS / pjlib,pjnath,pjsip steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installlinux.sh - name: install dependencies run: sudo apt-get update && sudo apt-get install -y --fix-missing swig libgnutls28-dev - name: configure @@ -142,12 +198,22 @@ jobs: run: make pjnath-test - name: pjsip-test run: make pjsip-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts vid-openh264-1: runs-on: ubuntu-latest name: OpenH264+VPX / pjmedia,pjsua steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installlinux.sh - name: install dependencies run: sudo apt-get update && sudo apt-get install -y swig nasm sip-tester libvpx-dev libopencore-amrnb-dev libsdl2-dev - name: get openh264 @@ -185,16 +251,29 @@ jobs: # SDL error: no available vid dev #- name: ensure SDL is installed # run: cat pjsua-caps | grep -E '\[SDL\]\[render\]' + # one of the step above has changed core_pattern, restore it + - name: reinstall cirunner + run: cirunner/installlinux.sh - name: pjmedia-test run: make pjmedia-test - name: pjsua-test run: make pjsua-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts vid-openh264-2: runs-on: ubuntu-latest name: OpenH264+VPX / pjlib,util,pjnath steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installlinux.sh - name: install dependencies run: sudo apt-get update && sudo apt-get install -y nasm libvpx-dev libopencore-amrnb-dev libsdl2-dev - name: get openh264 @@ -207,18 +286,31 @@ jobs: run: CFLAGS="-g" LDFLAGS="-rdynamic" ./configure - name: make run: $MAKE_FAST + # one of the step above has changed core_pattern, restore it + - name: reinstall cirunner + run: cirunner/installlinux.sh - name: pjlib-test run: make pjlib-test - name: pjlib-util-test run: make pjlib-util-test - name: pjnath-test run: make pjnath-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts vid-openh264-3: runs-on: ubuntu-latest name: OpenH264+VPX / pjsip steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installlinux.sh - name: install dependencies run: sudo apt-get update && sudo apt-get install -y nasm libvpx-dev libopencore-amrnb-dev libsdl2-dev - name: get openh264 @@ -231,14 +323,27 @@ jobs: run: CFLAGS="-g" LDFLAGS="-rdynamic" ./configure - name: make run: $MAKE_FAST + # one of the step above has changed core_pattern, restore it + - name: reinstall cirunner + run: cirunner/installlinux.sh - name: pjsip-test run: make pjsip-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts vid-ffmpeg: runs-on: ubuntu-latest name: FFMPEG+x264 / pjmedia steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installlinux.sh - name: install dependencies run: sudo apt-get update && sudo apt-get install -y swig nasm libx264-dev libvpx-dev libsdl2-dev - name: get ffmpeg @@ -273,5 +378,14 @@ jobs: # SDL error: no available vid dev #- name: ensure SDL is installed # run: cat pjsua-caps | grep -E '\[SDL\]\[render\]' + # one of the step above has changed core_pattern, restore it + - name: reinstall cirunner + run: cirunner/installlinux.sh - name: pjmedia-test run: export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH && make pjmedia-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index 793c6fbc77..da90e835f8 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -8,6 +8,8 @@ on: env: CI_ARGS: -w 2 --shuffle --stdout-buf 1 CI_MODE: --ci-mode + # Setting CI_RUNNER to empty should disable cirunner + CI_RUNNER: python ${{ github.workspace }}/cirunner/cirunner.py -t 3600 -o ${{ github.workspace }}/artifacts -- MAKE_FAST: make -j 2 jobs: default-build: @@ -16,10 +18,20 @@ jobs: name: Default / build only steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installmac.sh - name: configure run: ./configure - name: make run: $MAKE_FAST + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts default-pjlib-util-pjmedia-pjnath: # full bundle: enable all codecs + AEC + DTLS @@ -27,6 +39,10 @@ jobs: name: Default / pjmedia,pjnath steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installmac.sh - name: install dependencies run: brew install openssl opencore-amr - name: config site @@ -41,12 +57,22 @@ jobs: run: make pjmedia-test - name: pjnath-test run: make pjnath-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts default-pjsua-test: runs-on: macos-latest name: Default / pjsua-test steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installmac.sh - name: install dependencies run: brew install openssl opencore-amr swig sipp - name: set up Python @@ -65,12 +91,22 @@ jobs: run: cd pjsip-apps/src/swig && make - name: pjsua-test run: make pjsua-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts default-pjsip-test: runs-on: macos-latest name: Default / pjlib,util,pjsip-test steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installmac.sh - name: install dependencies run: brew install openssl opencore-amr - name: config site @@ -87,12 +123,22 @@ jobs: run: make pjlib-util-test - name: pjsip-test run: make pjsip-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts openssl-1: runs-on: macos-latest name: OpenSSL / pjlib,util,pjnath,pjmedia steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installmac.sh - name: install dependencies run: brew install openssl swig - name: configure @@ -121,12 +167,22 @@ jobs: run: make pjmedia-test - name: pjnath-test run: make pjnath-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts openssl-2: runs-on: macos-latest name: OpenSSL / pjsip steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installmac.sh - name: install dependencies run: brew install openssl - name: configure @@ -143,12 +199,22 @@ jobs: run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep -E 'PJ_SSL_SOCK_IMP\s+:\s+1' - name: pjsip-test run: make pjsip-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts gnu-tls-1: runs-on: macos-latest name: GnuTLS / pjlib,util,pjmedia,pjnath steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installmac.sh - name: install dependencies run: brew install gnutls swig - name: configure @@ -173,12 +239,22 @@ jobs: run: make pjmedia-test - name: pjnath-test run: make pjnath-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts gnu-tls-2: runs-on: macos-latest name: GnuTLS / pjsip-test steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installmac.sh - name: install dependencies run: brew install gnutls swig - name: configure @@ -191,12 +267,22 @@ jobs: run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep -E 'PJ_SSL_SOCK_IMP\s+:\s+2' - name: pjsip-test run: make pjsip-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts video-openh264-1: runs-on: macos-latest name: Openh264+VPX / pjmedia,pjsua steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installmac.sh - name: install dependencies run: brew install openssl openh264 libvpx opencore-amr swig sipp - name: config site @@ -217,12 +303,22 @@ jobs: run: make pjmedia-test - name: pjsua-test run: make pjsua-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts video-openh264-2: runs-on: macos-latest name: Openh264+VPX / util,pjnath steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installmac.sh - name: install dependencies run: brew install openssl openh264 libvpx opencore-amr - name: config site @@ -237,12 +333,22 @@ jobs: run: make pjlib-util-test - name: pjnath-test run: make pjnath-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts video-openh264-3: runs-on: macos-latest name: Openh264+VPX / pjlib,pjsip steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installmac.sh - name: install dependencies run: brew install openssl openh264 libvpx opencore-amr - name: config site @@ -257,12 +363,22 @@ jobs: run: make pjlib-test - name: pjsip-test run: make pjsip-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts video-ffmpeg: runs-on: macos-latest name: FFMPEG+VPX+x264 / pjmedia steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installmac.sh - name: install dependencies run: brew install openssl x264 libvpx nasm swig - name: get ffmpeg @@ -285,12 +401,22 @@ jobs: run: cd pjsip-apps/src/swig && make - name: pjmedia-test run: make pjmedia-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts video-vid-toolbox: runs-on: macos-latest name: VPX+VidToolbox / pjmedia steps: - uses: actions/checkout@v2 + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installmac.sh - name: install dependencies run: brew install openssl libvpx swig - name: config site @@ -307,3 +433,9 @@ jobs: run: cd pjsip-apps/src/swig && make - name: pjmedia-test run: make pjmedia-test + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index 28e0c5e2bf..089fdcb2bd 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -8,12 +8,18 @@ on: env: CI_WIN_ARGS: -w 3 --shuffle CI_MODE: --ci-mode + # Setting CI_RUNNER to empty should disable cirunner + CI_RUNNER: python ${{ github.workspace }}/cirunner/cirunner.py -t 3600 -o ${{ github.workspace }}/artifacts -- jobs: default: runs-on: windows-latest name: Default / build only steps: - uses: actions/checkout@master + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installwindows.ps1 - name: get swig run: Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/swigwin-4.1.1.zip" -OutFile ".\swigwin.zip" shell: powershell @@ -43,12 +49,22 @@ jobs: cd pjsip-apps/build msbuild swig_java_pjsua2.vcxproj /p:PlatformToolset=v143 /p:Configuration=Debug-Dynamic /p:Platform=win32 /p:UseEnv=true shell: cmd + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts openssl-1: runs-on: windows-latest name: OpenSSL / pjlib,util,pjmedia steps: - uses: actions/checkout@master + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installwindows.ps1 - name: get openssl run: Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/openssl-1.1.1s-win.zip" -OutFile ".\openssl.zip" shell: powershell @@ -85,31 +101,38 @@ jobs: $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" cd pjlib/bin - $args = $env:CI_WIN_ARGS -split ' ' - ./pjlib-test-i386-Win32-vc14-Release.exe $args $env:CI_MODE + Invoke-Expression "$env:CI_RUNNER ./pjlib-test-i386-Win32-vc14-Release.exe $env:CI_WIN_ARGS $env:CI_MODE" shell: powershell - name: pjlib-util-test run: | $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" cd pjlib-util/bin - $args = $env:CI_WIN_ARGS -split ' ' - ./pjlib-util-test-i386-Win32-vc14-Release.exe $args + Invoke-Expression "$env:CI_RUNNER ./pjlib-util-test-i386-Win32-vc14-Release.exe $env:CI_WIN_ARGS" shell: powershell - name: pjmedia-test run: | $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" cd pjmedia/bin - $args = $env:CI_WIN_ARGS -split ' ' - ./pjmedia-test-i386-Win32-vc14-Release.exe $args + Invoke-Expression "$env:CI_RUNNER ./pjmedia-test-i386-Win32-vc14-Release.exe $env:CI_WIN_ARGS" shell: powershell + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts openssl-2: runs-on: windows-latest name: OpenSSL / pjsip,pjsua steps: - uses: actions/checkout@master + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installwindows.ps1 - name: get openssl run: Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/openssl-1.1.1s-win.zip" -OutFile ".\openssl.zip" shell: powershell @@ -167,8 +190,7 @@ jobs: $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" cd pjsip/bin - $args = $env:CI_WIN_ARGS -split ' ' - ./pjsip-test-i386-Win32-vc14-Release.exe $args + Invoke-Expression "$env:CI_RUNNER ./pjsip-test-i386-Win32-vc14-Release.exe $env:CI_WIN_ARGS" shell: powershell - name: python pjsua tests run: | @@ -179,12 +201,22 @@ jobs: cd tests/pjsua echo python runall.py shell: powershell + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts openssl-3: runs-on: windows-latest name: OpenSSL / pjnath steps: - uses: actions/checkout@master + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installwindows.ps1 - name: get openssl run: Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/openssl-1.1.1s-win.zip" -OutFile ".\openssl.zip" shell: powershell @@ -221,15 +253,24 @@ jobs: $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" cd pjnath/bin - $args = $env:CI_WIN_ARGS -split ' ' - ./pjnath-test-i386-Win32-vc14-Release.exe $args + Invoke-Expression "$env:CI_RUNNER ./pjnath-test-i386-Win32-vc14-Release.exe $env:CI_WIN_ARGS" shell: powershell + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts gnu-tls: runs-on: windows-latest name: GnuTLS / build only steps: - uses: actions/checkout@master + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installwindows.ps1 - name: get gnutls run: Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/gnutls-3.5.17-win.zip" -Outfile ".\gnutls.zip" shell: powershell @@ -263,12 +304,22 @@ jobs: set LIB=%LIB%;%GNUTLS_DIR%\lib msbuild pjproject-vs14.sln /p:PlatformToolset=v143 /p:Configuration=Release /p:Platform=win32 /p:UseEnv=true shell: cmd + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts vid-libvpx-schannel-1: runs-on: windows-latest name: VPX+SChannel / pjlib,util,pjmedia,pjnath steps: - uses: actions/checkout@master + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installwindows.ps1 - name: get vpx run: Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/vpx-1.12-win.zip" -Outfile "vpx.zip" shell: powershell @@ -341,39 +392,45 @@ jobs: $env:SDL_DIR = Get-Content .\sdl_dir.txt $env:PATH+=";$env:SDL_DIR\lib\x86;" cd pjlib/bin - $args = $env:CI_WIN_ARGS -split ' ' - ./pjlib-test-i386-Win32-vc14-Release.exe $args $env:CI_MODE + Invoke-Expression "$env:CI_RUNNER ./pjlib-test-i386-Win32-vc14-Release.exe $env:CI_WIN_ARGS $env:CI_MODE" shell: powershell - name: pjlib-util-test run: | $env:SDL_DIR = Get-Content .\sdl_dir.txt $env:PATH+=";$env:SDL_DIR\lib\x86;" cd pjlib-util/bin - $args = $env:CI_WIN_ARGS -split ' ' - ./pjlib-util-test-i386-Win32-vc14-Release.exe $args + Invoke-Expression "$env:CI_RUNNER ./pjlib-util-test-i386-Win32-vc14-Release.exe $env:CI_WIN_ARGS" shell: powershell - name: pjmedia-test run: | $env:SDL_DIR = Get-Content .\sdl_dir.txt $env:PATH+=";$env:SDL_DIR\lib\x86;" cd pjmedia/bin - $args = $env:CI_WIN_ARGS -split ' ' - ./pjmedia-test-i386-Win32-vc14-Release.exe $args + Invoke-Expression "$env:CI_RUNNER ./pjmedia-test-i386-Win32-vc14-Release.exe $env:CI_WIN_ARGS" shell: powershell - name: pjnath-test run: | $env:SDL_DIR = Get-Content .\sdl_dir.txt $env:PATH+=";$env:SDL_DIR\lib\x86;" cd pjnath/bin - $args = $env:CI_WIN_ARGS -split ' ' - ./pjnath-test-i386-Win32-vc14-Release.exe $args $env:CI_MODE + Invoke-Expression "$env:CI_RUNNER ./pjnath-test-i386-Win32-vc14-Release.exe $env:CI_WIN_ARGS $env:CI_MODE" shell: powershell + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts vid-libvpx-schannel-2: runs-on: windows-latest name: VPX+SChannel / pjsua steps: - uses: actions/checkout@master + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installwindows.ps1 - name: get vpx run: Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/vpx-1.12-win.zip" -Outfile "vpx.zip" shell: powershell @@ -462,12 +519,22 @@ jobs: cd tests/pjsua echo python runall.py shell: powershell + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts vid-libvpx-schannel-3: runs-on: windows-latest name: VPX+SChannel / pjsip steps: - uses: actions/checkout@master + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installwindows.ps1 - name: get vpx run: Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/vpx-1.12-win.zip" -Outfile "vpx.zip" shell: powershell @@ -529,15 +596,24 @@ jobs: $env:SDL_DIR = Get-Content .\sdl_dir.txt $env:PATH+=";$env:SDL_DIR\lib\x86;" cd pjsip/bin - $args = $env:CI_WIN_ARGS -split ' ' - ./pjsip-test-i386-Win32-vc14-Release.exe $args + Invoke-Expression "$env:CI_RUNNER ./pjsip-test-i386-Win32-vc14-Release.exe $env:CI_WIN_ARGS" shell: powershell + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts build-win-vid-ffmpeg: runs-on: windows-latest name: FFMPEG / build only steps: - uses: actions/checkout@master + - name: install cirunner + run: | + git clone --depth 1 https://github.com/pjsip/cirunner.git + cirunner/installwindows.ps1 - name: get ffmpeg run: Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/ffmpeg-5.1.2-win.zip" -Outfile "ffmpeg.zip" shell: powershell @@ -590,6 +666,12 @@ jobs: set LIB=%LIB%;%FFMPEG_DIR%\lib;%SDL_DIR%\lib\x86 msbuild pjproject-vs14.sln /p:PlatformToolset=v143 /p:Configuration=Release /p:Platform=win32 /p:UseEnv=true shell: cmd + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts iocp: runs-on: windows-latest @@ -620,3 +702,9 @@ jobs: $args = $env:CI_WIN_ARGS -split ' ' ./pjlib-test-i386-Win32-vc14-Release.exe $args $env:CI_MODE shell: powershell + - name: upload artifacts on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ github.run_id }} + path: artifacts diff --git a/Makefile b/Makefile index b1f379e701..a27f08ef24 100644 --- a/Makefile +++ b/Makefile @@ -104,19 +104,19 @@ xhdrid: selftest: pjlib-test pjlib-util-test pjnath-test pjmedia-test pjsip-test pjsua-test pjlib-test: pjlib/bin/pjlib-test-$(TARGET_NAME) - cd pjlib/build && ../bin/pjlib-test-$(TARGET_NAME) $(CI_ARGS) $(CI_MODE) + cd pjlib/build && $(CI_RUNNER) ../bin/pjlib-test-$(TARGET_NAME) $(CI_ARGS) $(CI_MODE) pjlib-util-test: pjlib-util/bin/pjlib-util-test-$(TARGET_NAME) - cd pjlib-util/build && ../bin/pjlib-util-test-$(TARGET_NAME) $(CI_ARGS) + cd pjlib-util/build && $(CI_RUNNER) ../bin/pjlib-util-test-$(TARGET_NAME) $(CI_ARGS) pjnath-test: pjnath/bin/pjnath-test-$(TARGET_NAME) - cd pjnath/build && ../bin/pjnath-test-$(TARGET_NAME) $(CI_ARGS) + cd pjnath/build && $(CI_RUNNER) ../bin/pjnath-test-$(TARGET_NAME) $(CI_ARGS) pjmedia-test: pjmedia/bin/pjmedia-test-$(TARGET_NAME) - cd pjmedia/build && ../bin/pjmedia-test-$(TARGET_NAME) $(CI_ARGS) + cd pjmedia/build && $(CI_RUNNER) ../bin/pjmedia-test-$(TARGET_NAME) $(CI_ARGS) pjsip-test: pjsip/bin/pjsip-test-$(TARGET_NAME) - cd pjsip/build && ../bin/pjsip-test-$(TARGET_NAME) $(CI_ARGS) + cd pjsip/build && $(CI_RUNNER) ../bin/pjsip-test-$(TARGET_NAME) $(CI_ARGS) pjsua-test: cmp_wav cd tests/pjsua && python runall.py -t 2 diff --git a/pjlib-util/src/pjlib-util-test/main.c b/pjlib-util/src/pjlib-util-test/main.c index 7c4ec7a049..f61111028d 100644 --- a/pjlib-util/src/pjlib-util-test/main.c +++ b/pjlib-util/src/pjlib-util-test/main.c @@ -20,50 +20,6 @@ #include #include - -#if defined(PJ_SUNOS) && PJ_SUNOS!=0 - -#include -static void init_signals() -{ - struct sigaction act; - - memset(&act, 0, sizeof(act)); - act.sa_handler = SIG_IGN; - - sigaction(SIGALRM, &act, NULL); -} - -#elif (PJ_LINUX || PJ_DARWINOS) && defined(PJ_HAS_EXECINFO_H) && PJ_HAS_EXECINFO_H != 0 - -#include -#include -#include -#include -#include -static void print_stack(int sig) -{ - void *array[16]; - size_t size; - - size = backtrace(array, 16); - fprintf(stderr, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, STDERR_FILENO); - exit(1); -} - -static void init_signals(void) -{ - signal(SIGSEGV, &print_stack); - signal(SIGABRT, &print_stack); -} - -#else - -#define init_signals() - -#endif - #define boost() static void usage() @@ -78,7 +34,6 @@ static void usage() ut_usage(); puts(" -i Ask ENTER before quitting"); - puts(" -n Do not trap signals"); } @@ -86,7 +41,6 @@ int main(int argc, char *argv[]) { int rc; int interractive = 0; - int no_trap = 0; boost(); @@ -100,14 +54,9 @@ int main(int argc, char *argv[]) ut_app_init0(&test_app.ut_app); interractive = pj_argparse_get_bool(&argc, argv, "-i"); - no_trap = pj_argparse_get_bool(&argc, argv, "-n"); if (ut_parse_args(&test_app.ut_app, &argc, argv)) return 1; - if (!no_trap) { - init_signals(); - } - rc = test_main(argc, argv); if (interractive) { diff --git a/pjlib/src/pjlib-test/main.c b/pjlib/src/pjlib-test/main.c index 68cd44a6ef..802841c8a6 100644 --- a/pjlib/src/pjlib-test/main.c +++ b/pjlib/src/pjlib-test/main.c @@ -36,47 +36,6 @@ static void boost(void) #endif -#if defined(PJ_SUNOS) && PJ_SUNOS!=0 - -#include -static void init_signals() -{ - struct sigaction act; - - memset(&act, 0, sizeof(act)); - act.sa_handler = SIG_IGN; - - sigaction(SIGALRM, &act, NULL); -} - -#elif (PJ_LINUX || PJ_DARWINOS) && defined(PJ_HAS_EXECINFO_H) && PJ_HAS_EXECINFO_H != 0 - -#include -#include -#include -#include -#include -static void print_stack(int sig) -{ - void *array[16]; - size_t size; - - size = backtrace(array, 16); - fprintf(stderr, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, STDERR_FILENO); - exit(1); -} - -static void init_signals(void) -{ - signal(SIGSEGV, &print_stack); - signal(SIGABRT, &print_stack); -} - -#else -#define init_signals() -#endif - static void usage() { puts("Usage:"); @@ -91,7 +50,6 @@ static void usage() puts(" --skip-e Skip essential tests"); puts(" --ci-mode Running in slow CI mode"); puts(" -i Ask ENTER before quitting"); - puts(" -n Do not trap signals"); puts(" -p PORT Use port PORT for echo port"); puts(" -s SERVER Use SERVER as ech oserver"); puts(" -t ucp,tcp Set echo socket type to UDP or TCP"); @@ -102,7 +60,6 @@ int main(int argc, char *argv[]) { int rc; int interractive = 0; - int no_trap = 0; boost(); ut_app_init0(&test_app.ut_app); @@ -117,7 +74,6 @@ int main(int argc, char *argv[]) return 0; } interractive = pj_argparse_get_bool(&argc, argv, "-i"); - no_trap = pj_argparse_get_bool(&argc, argv, "-n"); if (pj_argparse_get_int(&argc, argv, "-p", &test_app.param_echo_port)) { usage(); return 1; @@ -157,10 +113,6 @@ int main(int argc, char *argv[]) test_app.param_ci_mode = pj_argparse_get_bool(&argc, argv, "--ci-mode"); - if (!no_trap) { - init_signals(); - } - if (pj_argparse_peek_next_option(argv)) { printf("Error: unknown argument %s\n", pj_argparse_peek_next_option(argv)); diff --git a/pjlib/src/pjlib-test/test_util.h b/pjlib/src/pjlib-test/test_util.h index 28f5b08a8b..a6714718fc 100644 --- a/pjlib/src/pjlib-test/test_util.h +++ b/pjlib/src/pjlib-test/test_util.h @@ -6,6 +6,8 @@ #include #include +#include + /* Overrideable max tests */ #ifndef UT_MAX_TESTS # define UT_MAX_TESTS 16 @@ -48,6 +50,12 @@ typedef struct ut_app_t /* Call this in main.c before parsing arguments */ PJ_INLINE(void) ut_app_init0(ut_app_t *ut_app) { + /* Disable buffering because sometimes output is not captured/shown + * (by cirunner) if the test program crashes too soon. + */ + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + pj_bzero(ut_app, sizeof(*ut_app)); ut_app->prm_logging_policy = PJ_TEST_FAILED_TESTS; ut_app->prm_nthreads = -1; diff --git a/pjmedia/src/test/main.c b/pjmedia/src/test/main.c index f0ee9ca799..779cbd0703 100644 --- a/pjmedia/src/test/main.c +++ b/pjmedia/src/test/main.c @@ -32,34 +32,6 @@ #endif -#if (PJ_LINUX || PJ_DARWINOS) && defined(PJ_HAS_EXECINFO_H) && PJ_HAS_EXECINFO_H != 0 - -#include -#include -#include -#include -#include -static void print_stack(int sig) -{ - void *array[16]; - size_t size; - - size = backtrace(array, 16); - fprintf(stderr, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, STDERR_FILENO); - exit(1); -} - -static void init_signals(void) -{ - signal(SIGSEGV, &print_stack); - signal(SIGABRT, &print_stack); -} - -#else -#define init_signals() -#endif - static void usage() { puts("Usage:"); @@ -72,7 +44,6 @@ static void usage() ut_usage(); puts(" -i Ask ENTER before quitting"); - puts(" -n Do not trap signals"); } @@ -80,7 +51,6 @@ static int main_func(int argc, char *argv[]) { int rc; int interractive = 0; - int no_trap = 0; if (pj_argparse_get_bool(&argc, argv, "-h") || pj_argparse_get_bool(&argc, argv, "--help")) @@ -92,14 +62,9 @@ static int main_func(int argc, char *argv[]) ut_app_init0(&test_app.ut_app); interractive = pj_argparse_get_bool(&argc, argv, "-i"); - no_trap = pj_argparse_get_bool(&argc, argv, "-n"); if (ut_parse_args(&test_app.ut_app, &argc, argv)) return 1; - if (!no_trap) { - init_signals(); - } - rc = test_main(argc, argv); if (interractive) { diff --git a/pjnath/src/pjnath-test/main.c b/pjnath/src/pjnath-test/main.c index 7f184dd568..354cdd682e 100644 --- a/pjnath/src/pjnath-test/main.c +++ b/pjnath/src/pjnath-test/main.c @@ -19,47 +19,6 @@ #include "test.h" #include -#if defined(PJ_SUNOS) && PJ_SUNOS!=0 - -#include -static void init_signals() -{ - struct sigaction act; - - memset(&act, 0, sizeof(act)); - act.sa_handler = SIG_IGN; - - sigaction(SIGALRM, &act, NULL); -} - -#elif (PJ_LINUX || PJ_DARWINOS) && defined(PJ_HAS_EXECINFO_H) && PJ_HAS_EXECINFO_H != 0 - -#include -#include -#include -#include -#include -static void print_stack(int sig) -{ - void *array[16]; - size_t size; - - size = backtrace(array, 16); - fprintf(stderr, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, STDERR_FILENO); - exit(1); -} - -static void init_signals(void) -{ - signal(SIGSEGV, &print_stack); - signal(SIGABRT, &print_stack); -} - -#else -#define init_signals() -#endif - #define boost() static void usage() @@ -74,14 +33,12 @@ static void usage() ut_usage(); puts(" -i Ask ENTER before quitting"); - puts(" -n Do not trap signals"); } int main(int argc, char *argv[]) { int rc; int interractive = 0; - int no_trap = 0; boost(); @@ -95,14 +52,9 @@ int main(int argc, char *argv[]) ut_app_init0(&test_app.ut_app); interractive = pj_argparse_get_bool(&argc, argv, "-i"); - no_trap = pj_argparse_get_bool(&argc, argv, "-n"); if (ut_parse_args(&test_app.ut_app, &argc, argv)) return 1; - if (!no_trap) { - init_signals(); - } - rc = test_main(argc, argv); if (interractive) { diff --git a/pjsip/src/test/main.c b/pjsip/src/test/main.c index cf17b5e177..24d9e1b893 100644 --- a/pjsip/src/test/main.c +++ b/pjsip/src/test/main.c @@ -55,39 +55,10 @@ static void usage(void) puts(" -s,--system NAME Set system name to NAME"); } -#if (PJ_LINUX || PJ_DARWINOS) && defined(PJ_HAS_EXECINFO_H) && PJ_HAS_EXECINFO_H != 0 - -#include -#include -#include -#include -#include -static void print_stack(int sig) -{ - void *array[16]; - size_t size; - - size = backtrace(array, 16); - fprintf(stderr, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, STDERR_FILENO); - exit(1); -} - -static void init_signals(void) -{ - signal(SIGSEGV, &print_stack); - signal(SIGABRT, &print_stack); -} - -#else -#define init_signals() -#endif - int main(int argc, char *argv[]) { int interractive = 0; int retval; - int no_trap = 0; warn(); @@ -101,7 +72,6 @@ int main(int argc, char *argv[]) ut_app_init0(&test_app.ut_app); interractive = pj_argparse_get_bool(&argc, argv, "-i"); - no_trap = pj_argparse_get_bool(&argc, argv, "-n"); if (pj_argparse_get_str(&argc, argv, "-s", (char**)&system_name) || pj_argparse_get_str(&argc, argv, "--system", (char**)&system_name)) { @@ -117,10 +87,6 @@ int main(int argc, char *argv[]) if (ut_parse_args(&test_app.ut_app, &argc, argv)) return 1; - if (!no_trap) { - init_signals(); - } - retval = test_main(argc, argv); if (interractive) { From 99b4d1e21f12f95e8a4fbc9b804e834d97404c4d Mon Sep 17 00:00:00 2001 From: sauwming Date: Thu, 6 Feb 2025 13:10:38 +0800 Subject: [PATCH 189/491] Fixed issue with SDP version when reoffer is rejected (#4289) --- pjmedia/src/pjmedia/sdp_neg.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pjmedia/src/pjmedia/sdp_neg.c b/pjmedia/src/pjmedia/sdp_neg.c index ecc1911281..dead49a1b9 100644 --- a/pjmedia/src/pjmedia/sdp_neg.c +++ b/pjmedia/src/pjmedia/sdp_neg.c @@ -1940,15 +1940,18 @@ PJ_DEF(pj_status_t) pjmedia_sdp_neg_cancel_offer(pjmedia_sdp_neg *neg) neg->state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER, PJMEDIA_SDPNEG_EINSTATE); - // No longer needed after #3322. - if (0 && neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER && + if (neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER && neg->active_local_sdp) { /* Increment next version number. This happens if for example * the reinvite offer is rejected by 488. If we don't increment * the version here, the next offer will have the same version. */ - neg->active_local_sdp->origin.version++; + // No longer needed after #3322. + // neg->active_local_sdp->origin.version++; + + /* Revert last sent SDP. */ + neg->last_sent = neg->active_local_sdp; } /* Revert back initial SDP */ From 2fff775f2047b2d7ce04ea680827a784cf1c8078 Mon Sep 17 00:00:00 2001 From: sauwming Date: Thu, 6 Feb 2025 13:11:01 +0800 Subject: [PATCH 190/491] Add API to register custom SDP comparison callback (#4286) --- pjmedia/include/pjmedia/sdp.h | 42 ++++++++++++++++++++++++++++++++++- pjmedia/src/pjmedia/sdp_cmp.c | 36 +++++++++++++++++++++++++----- 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/pjmedia/include/pjmedia/sdp.h b/pjmedia/include/pjmedia/sdp.h index e2e325438d..ab25c10123 100644 --- a/pjmedia/include/pjmedia/sdp.h +++ b/pjmedia/include/pjmedia/sdp.h @@ -805,7 +805,13 @@ pjmedia_sdp_session_clone( pj_pool_t *pool, /** - * Compare two SDP session for equality. + * Compare two SDP session for equality, by comparing: + * - the fields origin, subject, connection, time + * - the attributes direction, fmtp, and rtpmap + * - the media descriptions (see #pjmedia_sdp_media_cmp()) + * + * The function will also call the callback + * \a pjmedia_sdp_session_cmp_cb() if registered. * * @param sd1 The first SDP session to compare. * @param sd2 The second SDP session to compare. @@ -814,12 +820,46 @@ pjmedia_sdp_session_clone( pj_pool_t *pool, * @return PJ_SUCCESS when both SDPs are equal, or otherwise * the status code indicates which part of the session * descriptors are not equal. + * If \a pjmedia_sdp_session_cmp_cb() is registered, + * will return the status output parameter of the callback. */ PJ_DECL(pj_status_t) pjmedia_sdp_session_cmp(const pjmedia_sdp_session *sd1, const pjmedia_sdp_session *sd2, unsigned option); +/** + * The declaration of customized SDP session comparison callback. See + * #pjmedia_sdp_session_register_cmp_cb() for more info. + * + * @param sd1 The first SDP session to compare. + * @param sd2 The second SDP session to compare. + * @param option Must be zero for now. + * @param status Status code to be returned for the SDP comparison result + * (PJ_SUCCESS meaning both SDPs are equal). + * On input, it contains the return status of + * #pjmedia_sdp_session_cmp(). + */ +typedef void (*pjmedia_sdp_session_cmp_cb)(const pjmedia_sdp_session *sd1, + const pjmedia_sdp_session *sd2, + unsigned option, + pj_status_t *status); + + +/** + * Register customized SDP session comparison callback. The callback will + * be called by #pjmedia_sdp_session_cmp(). + * To unregister, just call this function with parameter cb set to NULL. + * + * @param cb The customized SDP session comparison callback or + * NULL to unregister the callback. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_sdp_session_register_cmp_cb(pjmedia_sdp_session_cmp_cb cb); + + /** * Add new attribute to the session descriptor. * diff --git a/pjmedia/src/pjmedia/sdp_cmp.c b/pjmedia/src/pjmedia/sdp_cmp.c index dfa3f33970..1d2151e4d2 100644 --- a/pjmedia/src/pjmedia/sdp_cmp.c +++ b/pjmedia/src/pjmedia/sdp_cmp.c @@ -22,6 +22,9 @@ #include +/* The registered customized SDP session comparison callback */ +static pjmedia_sdp_session_cmp_cb sdp_cmp_cb; + /* Compare connection line. */ static pj_status_t compare_conn(const pjmedia_sdp_conn *c1, const pjmedia_sdp_conn *c2) @@ -218,12 +221,9 @@ PJ_DEF(pj_status_t) pjmedia_sdp_media_cmp( const pjmedia_sdp_media *sd1, return PJ_SUCCESS; } -/* - * Compare two SDP session for equality. - */ -PJ_DEF(pj_status_t) pjmedia_sdp_session_cmp( const pjmedia_sdp_session *sd1, - const pjmedia_sdp_session *sd2, - unsigned option) +static pj_status_t sdp_session_cmp(const pjmedia_sdp_session *sd1, + const pjmedia_sdp_session *sd2, + unsigned option) { unsigned i; pj_status_t status; @@ -295,6 +295,30 @@ PJ_DEF(pj_status_t) pjmedia_sdp_session_cmp( const pjmedia_sdp_session *sd1, return PJ_SUCCESS; } +/* + * Compare two SDP session for equality. + */ +PJ_DEF(pj_status_t) pjmedia_sdp_session_cmp( const pjmedia_sdp_session *sd1, + const pjmedia_sdp_session *sd2, + unsigned option) +{ + pj_status_t status; + + status = sdp_session_cmp(sd1, sd2, option); + if (sdp_cmp_cb) { + (*sdp_cmp_cb)(sd1, sd2, option, &status); + } + + return status; +} + +/* Register customized SDP session comparison callback function. */ +PJ_DEF(pj_status_t) +pjmedia_sdp_session_register_cmp_cb(pjmedia_sdp_session_cmp_cb cb) +{ + sdp_cmp_cb = cb; + return PJ_SUCCESS; +} PJ_DEF(pj_status_t) pjmedia_sdp_conn_cmp(const pjmedia_sdp_conn *conn1, const pjmedia_sdp_conn *conn2, From e6196ad3930d2e68b679a652606797332fba2538 Mon Sep 17 00:00:00 2001 From: LeonidGoltsblat <138720759+LeonidGoltsblat@users.noreply.github.com> Date: Fri, 7 Feb 2025 02:15:10 +0300 Subject: [PATCH 191/491] Aligned memory allocation (#4277) * aligned memory allocaion * Fix alt API implementations (PJ_HAS_POOL_ALT_API) * pool test: add testing for bug in pj_pool_allocate_find with big alignment, and refactor to use unit test API * misc fixes on code review * pool_dbg alignment support + incompatible tests disabled for PJ_HAS_POOL_ALT_API --------- Co-authored-by: bennylp --- pjlib/include/pj/pool.h | 91 +++++++++++++++++-- pjlib/include/pj/pool_alt.h | 13 ++- pjlib/include/pj/pool_i.h | 72 ++++++++++++--- pjlib/src/pj/pool.c | 43 +++++---- pjlib/src/pj/pool_buf.c | 2 + pjlib/src/pj/pool_caching.c | 14 +-- pjlib/src/pj/pool_dbg.c | 33 +++++-- pjlib/src/pjlib-test/pool.c | 174 +++++++++++++++++++++--------------- 8 files changed, 319 insertions(+), 123 deletions(-) diff --git a/pjlib/include/pj/pool.h b/pjlib/include/pj/pool.h index 969a486ea9..c64a153fe1 100644 --- a/pjlib/include/pj/pool.h +++ b/pjlib/include/pj/pool.h @@ -331,6 +331,10 @@ struct pj_pool_t /** The callback to be called when the pool is unable to allocate memory. */ pj_pool_callback *callback; + /** The default alignment of memory block allocated from this pool + * (must be power of 2). */ + pj_size_t alignment; + }; @@ -347,8 +351,9 @@ struct pj_pool_t #endif /** - * Create a new pool from the pool factory. This wrapper will call create_pool - * member of the pool factory. + * Create a new pool from the pool factory. This wrapper will call + * pj_pool_aligned_create() with alignment parameter set to 0 + * which means use the default alignment (PJ_POOL_ALIGNMENT). * * @param factory The pool factory. * @param name The name to be assigned to the pool. The name should @@ -379,6 +384,47 @@ PJ_IDECL(pj_pool_t*) pj_pool_create(pj_pool_factory *factory, pj_size_t increment_size, pj_pool_callback *callback); + +/** + * Create a new pool from the pool factory. This wrapper will call create_pool + * member of the pool factory. + * + * @param factory The pool factory. + * @param name The name to be assigned to the pool. The name should + * not be longer than PJ_MAX_OBJ_NAME (32 chars), or + * otherwise it will be truncated. + * @param initial_size The size of initial memory blocks taken by the pool. + * Note that the pool will take 68+20 bytes for + * administrative area from this block. + * @param increment_size the size of each additional blocks to be allocated + * when the pool is running out of memory. If user + * requests memory which is larger than this size, then + * an error occurs. + * Note that each time a pool allocates additional block, + * it needs PJ_POOL_SIZE more to store some + * administrative info. + * @param alignment the default alignment of memory block allocated + * from this pool (must be power of 2). + * The actual allocation alignment will be at least equal + * to the alignment argument of the function, + * but not less than PJ_POOL_ALIGNMENT. + * Value of 0 means use PJ_POOL_ALIGNMENT. + * @param callback Callback to be called when error occurs in the pool. + * If this value is NULL, then the callback from pool + * factory policy will be used. + * Note that when an error occurs during pool creation, + * the callback itself is not called. Instead, NULL + * will be returned. + * + * @return The memory pool, or NULL. + */ +PJ_IDECL(pj_pool_t*) pj_pool_aligned_create(pj_pool_factory *factory, + const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_size_t alignment, + pj_pool_callback *callback); + /** * Release the pool back to pool factory. * @@ -448,8 +494,10 @@ PJ_IDECL(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool ); /** * Allocate storage with the specified size from the pool. + * The allocation will be aligned to the default alignment of the pool. * If there's no storage available in the pool, then the pool can allocate more * blocks if the increment size is larger than the requested size. + * This function will call pj_pool_aligned_alloc() with alignment set to 0. * * @param pool the pool. * @param size the requested size. @@ -460,6 +508,28 @@ PJ_IDECL(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool ); */ PJ_IDECL(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size); + +/** + * Allocate storage with the specified size and alignment from the pool. + * If there's no storage available in the pool, then the pool can allocate more + * blocks if the increment size is larger than the requested size. + * + * @param pool the pool. + * @param alignment the requested alignment of the allocation. + * The actual alignment of the allocation will be at least + * equal to the alignment argument of the function, + * but not less than the default pool alignment specified + * when the pool was created. + * Value of 0 means use the default alignment of this pool. + * @param size the requested size. + * + * @return pointer to the allocated memory. + * + * @see PJ_POOL_ALLOC_T + */ +PJ_IDECL(void *) pj_pool_aligned_alloc(pj_pool_t *pool, pj_size_t alignment, + pj_size_t size); + /** * Allocate storage from the pool, and initialize it to zero. * This function behaves like pj_pool_alloc(), except that the storage will @@ -523,9 +593,11 @@ PJ_INLINE(void*) pj_pool_zalloc(pj_pool_t *pool, pj_size_t size) * Internal functions */ /** Internal function */ -PJ_IDECL(void*) pj_pool_alloc_from_block(pj_pool_block *block, pj_size_t size); +PJ_IDECL(void*) pj_pool_alloc_from_block(pj_pool_block *block, pj_size_t alignment, + pj_size_t size); /** Internal function */ -PJ_DECL(void*) pj_pool_allocate_find(pj_pool_t *pool, pj_size_t size); +PJ_DECL(void*) pj_pool_allocate_find(pj_pool_t *pool, pj_size_t alignment, + pj_size_t size); @@ -687,7 +759,11 @@ struct pj_pool_factory * Note that each time a pool allocates additional block, * it needs 20 bytes (equal to sizeof(pj_pool_block)) to * store some administrative info. - * @param callback Cllback to be called when error occurs in the pool. + * @param alignment the default alignment of memory block allocated + * from this pool (must be power of 2). + * A value of 0 should result in the pool being created + * with alignment equal to PJ_POOL_ALIGNMENT. + * @param callback Callback to be called when error occurs in the pool. * Note that when an error occurs during pool creation, * the callback itself is not called. Instead, NULL * will be returned. @@ -698,6 +774,7 @@ struct pj_pool_factory const char *name, pj_size_t initial_size, pj_size_t increment_size, + pj_size_t alignment, pj_pool_callback *callback); /** @@ -748,6 +825,7 @@ struct pj_pool_factory * @param name Pool name. * @param initial_size Initial size. * @param increment_size Increment size. + * @param alignment Pool alignment. * @param callback Callback. * @return The pool object, or NULL. */ @@ -755,6 +833,7 @@ PJ_DECL(pj_pool_t*) pj_pool_create_int( pj_pool_factory *factory, const char *name, pj_size_t initial_size, pj_size_t increment_size, + pj_size_t alignment, pj_pool_callback *callback); /** @@ -762,11 +841,13 @@ PJ_DECL(pj_pool_t*) pj_pool_create_int( pj_pool_factory *factory, * @param pool The pool. * @param name Pool name. * @param increment_size Increment size. + * @param alignment Pool alignment. * @param callback Callback function. */ PJ_DECL(void) pj_pool_init_int( pj_pool_t *pool, const char *name, pj_size_t increment_size, + pj_size_t alignment, pj_pool_callback *callback); /** diff --git a/pjlib/include/pj/pool_alt.h b/pjlib/include/pj/pool_alt.h index 037e392d12..0af83b7f81 100644 --- a/pjlib/include/pj/pool_alt.h +++ b/pjlib/include/pj/pool_alt.h @@ -46,6 +46,7 @@ struct pj_pool_t char obj_name[32]; pj_size_t used_size; pj_pool_callback *cb; + pj_size_t alignment; }; @@ -68,7 +69,9 @@ PJ_DECL(int) pj_NO_MEMORY_EXCEPTION(void); * function. */ #define pj_pool_create(fc,nm,init,inc,cb) \ - pj_pool_create_imp(__FILE__, __LINE__, fc, nm, init, inc, cb) + pj_pool_create_imp(__FILE__, __LINE__, fc, nm, init, inc, 0, cb) +#define pj_pool_aligned_create(fc,nm,init,inc,alg,cb) \ + pj_pool_create_imp(__FILE__, __LINE__, fc, nm, init, inc, alg, cb) #define pj_pool_release(pool) pj_pool_release_imp(pool) #define pj_pool_safe_release(pool) pj_pool_safe_release_imp(pool) @@ -78,7 +81,9 @@ PJ_DECL(int) pj_NO_MEMORY_EXCEPTION(void); #define pj_pool_get_capacity(pool) pj_pool_get_capacity_imp(pool) #define pj_pool_get_used_size(pool) pj_pool_get_used_size_imp(pool) #define pj_pool_alloc(pool,sz) \ - pj_pool_alloc_imp(__FILE__, __LINE__, pool, sz) + pj_pool_alloc_imp(__FILE__, __LINE__, pool, 0, sz) +#define pj_pool_aligned_alloc(pool, alignment, sz) \ + pj_pool_alloc_imp(__FILE__, __LINE__, pool, alignment, sz) #define pj_pool_calloc(pool,cnt,elem) \ pj_pool_calloc_imp(__FILE__, __LINE__, pool, cnt, elem) @@ -98,6 +103,7 @@ PJ_DECL(pj_pool_t*) pj_pool_create_imp(const char *file, int line, const char *name, pj_size_t initial_size, pj_size_t increment_size, + pj_size_t alignment, pj_pool_callback *callback); /* Release pool */ @@ -123,7 +129,8 @@ PJ_DECL(pj_size_t) pj_pool_get_used_size_imp(pj_pool_t *pool); /* Allocate memory from the pool */ PJ_DECL(void*) pj_pool_alloc_imp(const char *file, int line, - pj_pool_t *pool, pj_size_t sz); + pj_pool_t *pool, pj_size_t alignment, + pj_size_t sz); /* Allocate memory from the pool and zero the memory */ PJ_DECL(void*) pj_pool_calloc_imp(const char *file, int line, diff --git a/pjlib/include/pj/pool_i.h b/pjlib/include/pj/pool_i.h index d19835f717..c019d8634a 100644 --- a/pjlib/include/pj/pool_i.h +++ b/pjlib/include/pj/pool_i.h @@ -20,6 +20,9 @@ #include +#define PJ_POOL_ALIGN_PTR(PTR,ALIGNMENT) (PTR + (-(pj_ssize_t)(PTR) & (ALIGNMENT-1))) +#define PJ_IS_POWER_OF_TWO(val) (((val)>0) && ((val) & ((val)-1))==0) +#define PJ_IS_ALIGNED(PTR, ALIGNMENT) (!((pj_ssize_t)(PTR) & ((ALIGNMENT)-1))) PJ_IDEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool ) { @@ -37,18 +40,26 @@ PJ_IDEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool ) return used_size; } -PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_block *block, pj_size_t size ) +PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_block *block, pj_size_t alignment, + pj_size_t size ) { - /* The operation below is valid for size==0. - * When size==0, the function will return the pointer to the pool - * memory address, but no memory will be allocated. - */ - if (size & (PJ_POOL_ALIGNMENT-1)) { - size = (size + PJ_POOL_ALIGNMENT) & ~(PJ_POOL_ALIGNMENT-1); - } - if ((pj_size_t)(block->end - block->cur) >= size) { - void *ptr = block->cur; - block->cur += size; + unsigned char *ptr; + + pj_assert(PJ_IS_POWER_OF_TWO(alignment) && PJ_IS_ALIGNED(size, alignment)); + // Size should be already aligned. + // this code was moved up to pj_pool_aligned_alloc. + ///* The operation below is valid for size==0. + // * When size==0, the function will return the pointer to the pool + // * memory address, but no memory will be allocated. + // */ + //if (size & (alignment -1)) { + // size = (size + alignment) & ~(alignment -1); + //} + ptr = PJ_POOL_ALIGN_PTR(block->cur, alignment); + if (ptr + size <= block->end && + /* here we check pointer overflow */ + block->cur <= ptr && ptr <= ptr + size) { + block->cur = ptr + size; return ptr; } return NULL; @@ -56,9 +67,32 @@ PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_block *block, pj_size_t size ) PJ_IDEF(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size) { - void *ptr = pj_pool_alloc_from_block(pool->block_list.next, size); + return pj_pool_aligned_alloc(pool, 0, size); +} + +PJ_IDECL(void *) pj_pool_aligned_alloc(pj_pool_t *pool, pj_size_t alignment, + pj_size_t size) +{ + void *ptr; + + PJ_ASSERT_RETURN(!alignment || PJ_IS_POWER_OF_TWO(alignment), NULL); + + if (alignment < pool->alignment) + alignment = pool->alignment; + + /* The operation below is valid for size==0. + * When size==0, the function will return the pointer to the pool + * memory address, but no memory will be allocated. + */ + if (size & (alignment -1)) { + size = (size + alignment) & ~(alignment -1); + } + pj_assert(PJ_IS_ALIGNED(size, alignment)); + + ptr = pj_pool_alloc_from_block(pool->block_list.next, + alignment, size); if (!ptr) - ptr = pj_pool_allocate_find(pool, size); + ptr = pj_pool_allocate_find(pool, alignment, size); return ptr; } @@ -82,7 +116,17 @@ PJ_IDEF(pj_pool_t*) pj_pool_create( pj_pool_factory *f, pj_size_t increment_size, pj_pool_callback *callback) { - return (*f->create_pool)(f, name, initial_size, increment_size, callback); + return pj_pool_aligned_create(f, name, initial_size, increment_size, 0, callback); +} + +PJ_IDECL(pj_pool_t *) pj_pool_aligned_create(pj_pool_factory *f, + const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_size_t alignment, + pj_pool_callback *callback) +{ + return (*f->create_pool)(f, name, initial_size, increment_size, alignment, callback); } PJ_IDEF(void) pj_pool_release( pj_pool_t *pool ) diff --git a/pjlib/src/pj/pool.c b/pjlib/src/pj/pool.c index 05d15aebb9..053257397b 100644 --- a/pjlib/src/pj/pool.c +++ b/pjlib/src/pj/pool.c @@ -31,7 +31,6 @@ #endif #define LOG(expr) PJ_LOG(6,expr) -#define ALIGN_PTR(PTR,ALIGNMENT) (PTR + (-(pj_ssize_t)(PTR) & (ALIGNMENT-1))) PJ_DEF_DATA(int) PJ_NO_MEMORY_EXCEPTION; @@ -72,7 +71,7 @@ static pj_pool_block *pj_pool_create_block( pj_pool_t *pool, pj_size_t size) block->end = ((unsigned char*)block) + size; /* Set the start pointer, aligning it as needed */ - block->cur = ALIGN_PTR(block->buf, PJ_POOL_ALIGNMENT); + block->cur = PJ_POOL_ALIGN_PTR(block->buf, pool->alignment); /* Insert in the front of the list. */ pj_list_insert_after(&pool->block_list, block); @@ -90,7 +89,8 @@ static pj_pool_block *pj_pool_create_block( pj_pool_t *pool, pj_size_t size) * a new block might be created (depending on whether the pool is allowed * to resize). */ -PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, pj_size_t size) +PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, pj_size_t alignment, + pj_size_t size) { pj_pool_block *block = pool->block_list.next; void *p; @@ -98,9 +98,10 @@ PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, pj_size_t size) unsigned i = 0; PJ_CHECK_STACK(); + pj_assert(PJ_IS_POWER_OF_TWO(alignment) && PJ_IS_ALIGNED(size, alignment)); while (block != &pool->block_list) { - p = pj_pool_alloc_from_block(block, size); + p = pj_pool_alloc_from_block(block, alignment, size); if (p != NULL) return p; @@ -131,14 +132,15 @@ PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, pj_size_t size) * the block. */ if (pool->increment_size < - size + sizeof(pj_pool_block) + PJ_POOL_ALIGNMENT) + sizeof(pj_pool_block)+ /*block header, itself may be unaligned*/ + alignment-1 + /* gap [0:alignment-1] to align first allocation*/ + size) /* allocation size, already aligned */ { pj_size_t count; - count = (size + pool->increment_size + sizeof(pj_pool_block) + - PJ_POOL_ALIGNMENT) / + count = (pool->increment_size + + sizeof(pj_pool_block) + alignment-1 + size) / pool->increment_size; block_size = count * pool->increment_size; - } else { block_size = pool->increment_size; } @@ -153,7 +155,7 @@ PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, pj_size_t size) if (!block) return NULL; - p = pj_pool_alloc_from_block(block, size); + p = pj_pool_alloc_from_block(block, alignment, size); pj_assert(p != NULL); #if PJ_DEBUG if (p == NULL) { @@ -166,15 +168,19 @@ PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, pj_size_t size) /* * Internal function to initialize pool. */ -PJ_DEF(void) pj_pool_init_int( pj_pool_t *pool, - const char *name, - pj_size_t increment_size, - pj_pool_callback *callback) +PJ_DEF(void) pj_pool_init_int(pj_pool_t *pool, + const char *name, + pj_size_t increment_size, + pj_size_t alignment, + pj_pool_callback *callback) { + PJ_CHECK_STACK(); + pj_assert(!alignment || PJ_IS_POWER_OF_TWO(alignment)); pool->increment_size = increment_size; pool->callback = callback; + pool->alignment = (alignment < PJ_POOL_ALIGNMENT) ? PJ_POOL_ALIGNMENT : alignment; if (name) { char *p = pj_ansi_strchr(name, '%'); @@ -196,6 +202,7 @@ PJ_DEF(void) pj_pool_init_int( pj_pool_t *pool, PJ_DEF(pj_pool_t*) pj_pool_create_int( pj_pool_factory *f, const char *name, pj_size_t initial_size, pj_size_t increment_size, + pj_size_t alignment, pj_pool_callback *callback) { pj_pool_t *pool; @@ -207,6 +214,10 @@ PJ_DEF(pj_pool_t*) pj_pool_create_int( pj_pool_factory *f, const char *name, /* Size must be at least sizeof(pj_pool)+sizeof(pj_pool_block) */ PJ_ASSERT_RETURN(initial_size >= sizeof(pj_pool_t)+sizeof(pj_pool_block), NULL); + PJ_ASSERT_RETURN(!alignment || PJ_IS_POWER_OF_TWO(alignment), NULL); + + if (alignment < PJ_POOL_ALIGNMENT) + alignment = PJ_POOL_ALIGNMENT; /* If callback is NULL, set calback from the policy */ if (callback == NULL) @@ -230,11 +241,11 @@ PJ_DEF(pj_pool_t*) pj_pool_create_int( pj_pool_factory *f, const char *name, block->end = buffer + initial_size; /* Set the start pointer, aligning it as needed */ - block->cur = ALIGN_PTR(block->buf, PJ_POOL_ALIGNMENT); + block->cur = PJ_POOL_ALIGN_PTR(block->buf, alignment); pj_list_insert_after(&pool->block_list, block); - pj_pool_init_int(pool, name, increment_size, callback); + pj_pool_init_int(pool, name, increment_size, alignment, callback); /* Pool initial capacity and used size */ pool->capacity = initial_size; @@ -275,7 +286,7 @@ static void reset_pool(pj_pool_t *pool) block = pool->block_list.next; /* Set the start pointer, aligning it as needed */ - block->cur = ALIGN_PTR(block->buf, PJ_POOL_ALIGNMENT); + block->cur = PJ_POOL_ALIGN_PTR(block->buf, pool->alignment); pool->capacity = block->end - (unsigned char*)pool; } diff --git a/pjlib/src/pj/pool_buf.c b/pjlib/src/pj/pool_buf.c index 50a0fa57de..8ab4c75648 100644 --- a/pjlib/src/pj/pool_buf.c +++ b/pjlib/src/pj/pool_buf.c @@ -105,9 +105,11 @@ PJ_DEF(pj_pool_t*) pj_pool_create_on_buf(const char *name, pj_thread_local_set(tls, ¶m); return pj_pool_create_int(&stack_based_factory, name, size, 0, + PJ_POOL_ALIGNMENT, pj_pool_factory_default_policy.callback); #else PJ_UNUSED_ARG(buf); + PJ_UNUSED_ARG(pool_buf_initialize); return pj_pool_create(NULL, name, size, size, NULL); #endif } diff --git a/pjlib/src/pj/pool_caching.c b/pjlib/src/pj/pool_caching.c index fd48badfb2..b44bcb430e 100644 --- a/pjlib/src/pj/pool_caching.c +++ b/pjlib/src/pj/pool_caching.c @@ -31,6 +31,7 @@ static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, const char *name, pj_size_t initial_size, pj_size_t increment_sz, + pj_size_t alignment, pj_pool_callback *callback); static void cpool_release_pool(pj_pool_factory *pf, pj_pool_t *pool); static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail ); @@ -127,10 +128,11 @@ PJ_DEF(void) pj_caching_pool_destroy( pj_caching_pool *cp ) } static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, - const char *name, - pj_size_t initial_size, - pj_size_t increment_sz, - pj_pool_callback *callback) + const char *name, + pj_size_t initial_size, + pj_size_t increment_sz, + pj_size_t alignment, + pj_pool_callback *callback) { pj_caching_pool *cp = (pj_caching_pool*)pf; pj_pool_t *pool; @@ -173,7 +175,7 @@ static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, /* Create new pool */ pool = pj_pool_create_int(&cp->factory, name, initial_size, - increment_sz, callback); + increment_sz, alignment, callback); if (!pool) { pj_lock_release(cp->lock); return NULL; @@ -185,7 +187,7 @@ static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, pj_list_erase(pool); /* Initialize the pool. */ - pj_pool_init_int(pool, name, increment_sz, callback); + pj_pool_init_int(pool, name, increment_sz, alignment, callback); /* Update pool manager's free capacity. */ if (cp->capacity > pj_pool_get_capacity(pool)) { diff --git a/pjlib/src/pj/pool_dbg.c b/pjlib/src/pj/pool_dbg.c index 5bc22c04cc..bd7db23095 100644 --- a/pjlib/src/pj/pool_dbg.c +++ b/pjlib/src/pj/pool_dbg.c @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #include #if PJ_HAS_POOL_ALT_API @@ -41,7 +42,7 @@ /* Uncomment this to enable TRACE_ */ //#undef TRACE_ - +#define IS_POWER_OF_TWO(val) (((val)>0) && ((val) & ((val)-1))==0) PJ_DEF_DATA(int) PJ_NO_MEMORY_EXCEPTION; @@ -57,6 +58,7 @@ PJ_DEF(pj_pool_t*) pj_pool_create_imp( const char *file, int line, const char *name, pj_size_t initial_size, pj_size_t increment_size, + pj_size_t alignment, pj_pool_callback *callback) { pj_pool_t *pool; @@ -67,6 +69,11 @@ PJ_DEF(pj_pool_t*) pj_pool_create_imp( const char *file, int line, PJ_UNUSED_ARG(initial_size); PJ_UNUSED_ARG(increment_size); + if (alignment < PJ_POOL_ALIGNMENT) + alignment = PJ_POOL_ALIGNMENT; + + PJ_ASSERT_RETURN(IS_POWER_OF_TWO(alignment), NULL); + pool = malloc(sizeof(struct pj_pool_t)); if (!pool) return NULL; @@ -87,6 +94,7 @@ PJ_DEF(pj_pool_t*) pj_pool_create_imp( const char *file, int line, pool->factory = NULL; pool->first_mem = NULL; pool->used_size = 0; + pool->alignment = alignment; pool->cb = callback; return pool; @@ -157,14 +165,27 @@ PJ_DEF(pj_size_t) pj_pool_get_used_size_imp(pj_pool_t *pool) /* Allocate memory from the pool */ PJ_DEF(void*) pj_pool_alloc_imp( const char *file, int line, - pj_pool_t *pool, pj_size_t sz) + pj_pool_t *pool, pj_size_t alignment, + pj_size_t sz) { struct pj_pool_mem *mem; + char *buf; PJ_UNUSED_ARG(file); PJ_UNUSED_ARG(line); - mem = malloc(sz + sizeof(struct pj_pool_mem)); + if (alignment < pool->alignment) + alignment = pool->alignment; + + PJ_ASSERT_RETURN(IS_POWER_OF_TWO(alignment), NULL); + + /* obey alignment request from user */ + if (sz & (alignment - 1)) { + sz = (sz + alignment) & ~(alignment - 1); + } + mem = malloc(sz + /* allocation size, already aligned */ + alignment-1 + /*gap [0:alignment-1] to align allocation*/ + sizeof(struct pj_pool_mem)); /*block header, may be unaligned*/ if (!mem) { if (pool->cb) (*pool->cb)(pool, sz); @@ -185,8 +206,8 @@ PJ_DEF(void*) pj_pool_alloc_imp( const char *file, int line, TRACE_(msg); } #endif - - return ((char*)mem) + sizeof(struct pj_pool_mem); + buf = ((char*)mem) + sizeof(struct pj_pool_mem); + return (buf + (-(pj_ssize_t)buf & (alignment-1))); } /* Allocate memory from the pool and zero the memory */ @@ -196,7 +217,7 @@ PJ_DEF(void*) pj_pool_calloc_imp( const char *file, int line, { void *mem; - mem = pj_pool_alloc_imp(file, line, pool, cnt*elemsz); + mem = pj_pool_alloc_imp(file, line, pool, 0, cnt*elemsz); if (!mem) return NULL; diff --git a/pjlib/src/pjlib-test/pool.c b/pjlib/src/pjlib-test/pool.c index 344ab28477..c2aac396e4 100644 --- a/pjlib/src/pjlib-test/pool.c +++ b/pjlib/src/pjlib-test/pool.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "test.h" /** @@ -38,7 +39,8 @@ #if INCLUDE_POOL_TEST -#define SIZE 4096 +#define THIS_FILE "pool.c" +#define SIZE 4096 /* Normally we should throw exception when memory alloc fails. * Here we do nothing so that the flow will go back to original caller, @@ -62,69 +64,107 @@ static int capacity_test(void) PJ_LOG(3,("test", "...capacity_test()")); - if (!pool) - return -200; + PJ_TEST_NOT_NULL(pool, NULL, return -200); freesize = GET_FREE(pool); - - if (pj_pool_alloc(pool, freesize) == NULL) { - PJ_LOG(3,("test", "...error: wrong freesize %lu reported", - (unsigned long)freesize)); - pj_pool_release(pool); - return -210; - } + PJ_TEST_NOT_NULL(pj_pool_alloc(pool, freesize), "wrong freesize reported", + { pj_pool_release(pool); return -210; }); pj_pool_release(pool); + return 0; } /* Test that the alignment works. */ static int pool_alignment_test(void) { - pj_pool_t *pool; + pj_pool_t *pool, *pool2; void *ptr; - enum { MEMSIZE = 64, LOOP = 100 }; + enum { MEMSIZE = 64, LOOP = 100, POOL_ALIGNMENT_TEST = 4*PJ_POOL_ALIGNMENT }; unsigned i; + int rc = 0; PJ_LOG(3,("test", "...alignment test")); + /* Test pj_pool_allocate_find() when alignment is so big */ + pool = pj_pool_aligned_create(mem, NULL, + 50 + sizeof(pj_pool_t) + sizeof(pj_pool_block), + 50, 4, + &null_callback); + ptr = pj_pool_aligned_alloc(pool, 256, 1); + pj_pool_release(pool); + PJ_TEST_NOT_NULL(ptr, NULL, return -300); + pool = pj_pool_create(mem, NULL, PJ_POOL_SIZE+MEMSIZE, MEMSIZE, NULL); - if (!pool) - return -300; + PJ_TEST_NOT_NULL(pool, NULL, return -304); + + pool2 = pj_pool_aligned_create(mem, NULL, PJ_POOL_SIZE + MEMSIZE, MEMSIZE, + POOL_ALIGNMENT_TEST, NULL); + PJ_TEST_NOT_NULL(pool2, NULL, { pj_pool_release(pool); return -307; }); #define IS_ALIGNED(p) ((((unsigned long)(pj_ssize_t)p) & \ (PJ_POOL_ALIGNMENT-1)) == 0) +#define IS_ALIGNED2(p) ((((unsigned long)(pj_ssize_t)p) & \ + (POOL_ALIGNMENT_TEST-1)) == 0) for (i=0; i 0) { @@ -199,30 +240,20 @@ static int drain_test(pj_size_t size, pj_size_t increment) size2 = (int)freesize; p = pj_pool_alloc(pool, size2); - if (!p) { - status=-20; goto on_error; - } + PJ_TEST_NOT_NULL(p, NULL, { status=-20; goto on_error;}); freesize -= size2; } /* Check that capacity is zero. */ - if (GET_FREE(pool) != 0) { - PJ_LOG(3,("test", "....error: returned free=%lu (expecting 0)", - (unsigned long)(GET_FREE(pool)))); - status=-30; goto on_error; - } + PJ_TEST_EQ(GET_FREE(pool), 0, NULL, { status=-30; goto on_error;}); /* Try to allocate once more */ p = pj_pool_alloc(pool, 257); - if (!p) { - status=-40; goto on_error; - } + PJ_TEST_NOT_NULL(p, NULL, { status=-40; goto on_error;}); /* Check that capacity is NOT zero. */ - if (GET_FREE(pool) == 0) { - status=-50; goto on_error; - } + PJ_TEST_NEQ(GET_FREE(pool), 0, NULL, { status=-50; goto on_error;}); on_error: @@ -244,29 +275,22 @@ static int pool_buf_test(void) PJ_LOG(3,("test", "...pool_buf test")); pool = pj_pool_create_on_buf("no name", buf, sizeof(buf)); - if (!pool) - return -70; + PJ_TEST_NOT_NULL(pool, NULL, return -70); /* Drain the pool */ PJ_TRY { - if ((p=pj_pool_alloc(pool, STATIC_BUF_SIZE/2)) == NULL) - return -75; - - if ((p=pj_pool_alloc(pool, STATIC_BUF_SIZE/2)) == NULL) - return -76; + PJ_TEST_NOT_NULL(pj_pool_alloc(pool, STATIC_BUF_SIZE/2), NULL, return -75); + PJ_TEST_NOT_NULL(pj_pool_alloc(pool, STATIC_BUF_SIZE/2), NULL, return -76); } PJ_CATCH_ANY { - return -77; + PJ_TEST_TRUE(PJ_FALSE, "Caught exception", return -77); } PJ_END; /* On the next alloc, exception should be thrown */ PJ_TRY { p = pj_pool_alloc(pool, STATIC_BUF_SIZE); - if (p != NULL) { - /* This is unexpected, the alloc should fail */ - return -78; - } + PJ_TEST_TRUE(p==NULL, NULL, return -78); } PJ_CATCH_ANY { /* This is the expected result */ @@ -281,11 +305,12 @@ static int pool_buf_test(void) int pool_test(void) { enum { LOOP = 2 }; - int loop; int rc; +#if PJ_HAS_POOL_ALT_API == 0 rc = capacity_test(); if (rc) return rc; +#endif //PJ_HAS_POOL_ALT_API == 0 rc = pool_alignment_test(); if (rc) return rc; @@ -293,6 +318,8 @@ int pool_test(void) rc = pool_buf_alignment_test(); if (rc) return rc; +#if PJ_HAS_POOL_ALT_API == 0 + int loop; for (loop=0; loop Date: Fri, 7 Feb 2025 07:18:09 +0800 Subject: [PATCH 192/491] Fixed Java make clean error (#4297) --- pjsip-apps/src/swig/java/Makefile | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pjsip-apps/src/swig/java/Makefile b/pjsip-apps/src/swig/java/Makefile index 172a35571d..3df969194b 100644 --- a/pjsip-apps/src/swig/java/Makefile +++ b/pjsip-apps/src/swig/java/Makefile @@ -40,7 +40,13 @@ else endif # Get JDK location -ifeq ("$(JAVA_HOME)","") +CHECK_JDK := 1 +ifeq ($(findstring clean,$(MAKECMDGOALS)),clean) + CHECK_JDK := 0 +endif + +ifeq ($(CHECK_JDK), 1) + ifeq ("$(JAVA_HOME)","") verify_jdk = $(shell test -d $(1)/include && test -d $(1)/lib && \ echo inclib1) \ @@ -121,7 +127,7 @@ ifeq ("$(JAVA_HOME)","") $(warning Cannot determine JDK binary path. Set to $(JAVA_BIN)) endif -else + else JAVA_INC := $(JAVA_HOME)/include JAVA_LIB := $(JAVA_HOME)/lib @@ -131,6 +137,7 @@ else JAVA_BIN := $(JAVA_HOME) endif + endif endif # Env settings, e.g: path to SWIG, JDK, java(.exe), javac(.exe) From 65c4bc98804cb32f43b5b0d44dcb0dd1d247e3be Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 7 Feb 2025 07:18:22 +0800 Subject: [PATCH 193/491] Fix pjsua sample app user agent (#4296) --- pjsip-apps/src/pjsua/pjsua_app_config.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pjsip-apps/src/pjsua/pjsua_app_config.c b/pjsip-apps/src/pjsua/pjsua_app_config.c index 76825a1bf1..b4920d89bf 100644 --- a/pjsip-apps/src/pjsua/pjsua_app_config.c +++ b/pjsip-apps/src/pjsua/pjsua_app_config.c @@ -1606,8 +1606,15 @@ static void default_config() pjsua_app_config *cfg = &app_config; pjsua_config_default(&cfg->cfg); - pj_ansi_snprintf(tmp, sizeof(tmp), "PJSUA v%s %s", pj_get_version(), + pj_ansi_snprintf(tmp, sizeof(tmp), "PJSUA/v%s %s", pj_get_version(), pj_get_sys_info()->info.ptr); + /* System info may contain more than one SLASH which doesn't conform + * with the RFC 3261, so just replace it with white space. + */ + for (i = sizeof("PJSUA/") + 1; ;i++) { + if (tmp[i] == 0) break; + if (tmp[i] == '/') tmp[i] = ' '; + } pj_strdup2_with_null(app_config.pool, &cfg->cfg.user_agent, tmp); pjsua_logging_config_default(&cfg->log_cfg); From 4ded10f97d9f3ed89dc453b95ce43170b1d338eb Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 7 Feb 2025 13:27:00 +0800 Subject: [PATCH 194/491] Fixed msg_data assertion in pjsua_acc_send_request() API (#4298) --- pjsip/src/pjsua-lib/pjsua_acc.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index 692f6e988e..e7fac2e786 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -1556,18 +1556,19 @@ PJ_DEF(pj_status_t) pjsua_acc_send_request(pjsua_acc_id acc_id, pjsip_tx_data *tdata = NULL; send_request_data *request_data = NULL; - PJ_ASSERT_RETURN(acc_id>=0, PJ_EINVAL); + PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc), + PJ_EINVAL); + PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL); PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL); PJ_ASSERT_RETURN(method, PJ_EINVAL); PJ_UNUSED_ARG(options); - PJ_ASSERT_RETURN(msg_data, PJ_EINVAL); PJ_LOG(4,(THIS_FILE, "Account %d sending %.*s request..", acc_id, (int)method->slen, method->ptr)); pj_log_push_indent(); pjsip_method_init_np(&method_, (pj_str_t*)method); - status = pjsua_acc_create_request(acc_id, &method_, &msg_data->target_uri, &tdata); + status = pjsua_acc_create_request(acc_id, &method_, dest_uri, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create request", status); goto on_return; @@ -1581,7 +1582,8 @@ PJ_DEF(pj_status_t) pjsua_acc_send_request(pjsua_acc_id acc_id, request_data->acc_id = acc_id; request_data->token = token; - pjsua_process_msg_data(tdata, msg_data); + if (msg_data) + pjsua_process_msg_data(tdata, msg_data); cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, NULL); if (cap_hdr) { @@ -3433,9 +3435,13 @@ PJ_DEF(pj_status_t) pjsua_acc_create_request(pjsua_acc_id acc_id, pjsip_tpselector tp_sel; pj_status_t status; + PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc), + PJ_EINVAL); PJ_ASSERT_RETURN(method && target && p_tdata, PJ_EINVAL); PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL); + PJSUA_LOCK(); + acc = &pjsua_var.acc[acc_id]; status = pjsip_endpt_create_request(pjsua_var.endpt, method, target, @@ -3443,6 +3449,7 @@ PJ_DEF(pj_status_t) pjsua_acc_create_request(pjsua_acc_id acc_id, NULL, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create request", status); + PJSUA_UNLOCK(); return status; } @@ -3477,6 +3484,8 @@ PJ_DEF(pj_status_t) pjsua_acc_create_request(pjsua_acc_id acc_id, &tdata->via_tp); } + PJSUA_UNLOCK(); + /* Done */ *p_tdata = tdata; return PJ_SUCCESS; From fdd4041f126de8b6b1f69c5bcbe35821011acd76 Mon Sep 17 00:00:00 2001 From: Maciej Lisowski <39798354+MaciejDromin@users.noreply.github.com> Date: Tue, 11 Feb 2025 12:48:17 +0100 Subject: [PATCH 195/491] Add missing OnTimerParam import in Android Example (#4299) --- .../app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java index ca9519c7dc..56ff9623ad 100644 --- a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java +++ b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java @@ -52,6 +52,7 @@ import org.pjsip.pjsua2.CallInfo; import org.pjsip.pjsua2.CallOpParam; import org.pjsip.pjsua2.OnCallMediaEventParam; +import org.pjsip.pjsua2.OnTimerParam; import org.pjsip.pjsua2.StringVector; import org.pjsip.pjsua2.pjsip_inv_state; import org.pjsip.pjsua2.pjsip_status_code; From 6cbf0e6b75f4a9887babe145604f4445a4097496 Mon Sep 17 00:00:00 2001 From: Riza Sulistyo Date: Wed, 12 Feb 2025 15:23:37 +0700 Subject: [PATCH 196/491] Check if ice strans is valid before using it to send (#4301) --- pjmedia/src/pjmedia/transport_ice.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pjmedia/src/pjmedia/transport_ice.c b/pjmedia/src/pjmedia/transport_ice.c index c8d1ae7516..cf0ba886ce 100644 --- a/pjmedia/src/pjmedia/transport_ice.c +++ b/pjmedia/src/pjmedia/transport_ice.c @@ -2458,8 +2458,12 @@ static pj_status_t transport_send_rtp(pjmedia_transport *tp, pj_size_t size) { struct transport_ice *tp_ice = (struct transport_ice*)tp; + pj_ice_strans *ice_st = tp_ice->ice_st; pj_status_t status; + if (!ice_st) + return PJ_SUCCESS; + /* Simulate packet lost on TX direction */ if (tp_ice->tx_drop_pct) { if ((pj_rand() % 100) <= (int)tp_ice->tx_drop_pct) { @@ -2470,7 +2474,7 @@ static pj_status_t transport_send_rtp(pjmedia_transport *tp, } } - status = pj_ice_strans_sendto2(tp_ice->ice_st, 1, + status = pj_ice_strans_sendto2(ice_st, 1, pkt, size, &tp_ice->remote_rtp, tp_ice->addr_len); if (status == PJ_EPENDING) @@ -2494,6 +2498,10 @@ static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, pj_size_t size) { struct transport_ice *tp_ice = (struct transport_ice*)tp; + pj_ice_strans *ice_st = tp_ice->ice_st; + + if (!ice_st) + return PJ_SUCCESS; if (tp_ice->comp_cnt > 1 || tp_ice->use_rtcp_mux) { pj_status_t status; @@ -2504,7 +2512,7 @@ static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, addr_len = pj_sockaddr_get_len(addr); } - status = pj_ice_strans_sendto2(tp_ice->ice_st, comp_id, pkt, size, + status = pj_ice_strans_sendto2(ice_st, comp_id, pkt, size, addr, addr_len); if (status == PJ_EPENDING) status = PJ_SUCCESS; From 10b4d30630cc6eed40d3eaeb2449284ed96bb877 Mon Sep 17 00:00:00 2001 From: sauwming Date: Thu, 13 Feb 2025 09:06:54 +0800 Subject: [PATCH 197/491] Audio and video stream refactoring (#4300) --- pjmedia/include/pjmedia/config.h | 14 +- pjmedia/include/pjmedia/stream.h | 67 +- pjmedia/include/pjmedia/stream_common.h | 329 +++++ pjmedia/include/pjmedia/vid_stream.h | 45 +- pjmedia/src/pjmedia/stream.c | 1522 ++++++----------------- pjmedia/src/pjmedia/stream_common.c | 530 ++++++++ pjmedia/src/pjmedia/stream_imp_common.c | 599 +++++++++ pjmedia/src/pjmedia/stream_info.c | 241 +--- pjmedia/src/pjmedia/vid_stream.c | 1155 ++++------------- pjmedia/src/pjmedia/vid_stream_info.c | 228 +--- pjsip/src/pjsua-lib/pjsua_media.c | 815 +++++------- 11 files changed, 2450 insertions(+), 3095 deletions(-) create mode 100755 pjmedia/src/pjmedia/stream_imp_common.c diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h index 86746c81c1..9656b6f31f 100644 --- a/pjmedia/include/pjmedia/config.h +++ b/pjmedia/include/pjmedia/config.h @@ -641,7 +641,7 @@ #endif /** - * Perform RTP payload type checking in the audio stream. Normally the peer + * Perform RTP payload type checking in the media stream. Normally the peer * MUST send RTP with payload type as we specified in our SDP. Certain * agents may not be able to follow this hence the only way to have * communication is to disable this check. @@ -1706,12 +1706,18 @@ * agents may not be able to follow this hence the only way to have * communication is to disable this check. * - * Default: PJMEDIA_STREAM_CHECK_RTP_PT (follow audio stream's setting) + * Note: Since media streams now share some common implementation, + * the setting MUST have the same value as PJMEDIA_STREAM_CHECK_RTP_PT. */ -#ifndef PJMEDIA_VID_STREAM_CHECK_RTP_PT -# define PJMEDIA_VID_STREAM_CHECK_RTP_PT PJMEDIA_STREAM_CHECK_RTP_PT +#if defined(PJMEDIA_VID_STREAM_CHECK_RTP_PT) && \ + PJMEDIA_VID_STREAM_CHECK_RTP_PT != PJMEDIA_STREAM_CHECK_RTP_PT +# pragma message("PJMEDIA_VID_STREAM_CHECK_RTP_PT must have the same " \ + "value as PJMEDIA_STREAM_CHECK_RTP_PT...") #endif +#undef PJMEDIA_VID_STREAM_CHECK_RTP_PT +#define PJMEDIA_VID_STREAM_CHECK_RTP_PT PJMEDIA_STREAM_CHECK_RTP_PT + /** * @} */ diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h index dc90d62022..43af19b000 100644 --- a/pjmedia/include/pjmedia/stream.h +++ b/pjmedia/include/pjmedia/stream.h @@ -78,79 +78,20 @@ PJ_BEGIN_DECL */ /** - * Opaque declaration for media channel. - * Media channel is unidirectional flow of media from sender to - * receiver. - */ -typedef struct pjmedia_channel pjmedia_channel; - -/** - * This structure describes media stream information. Each media stream + * This structure describes audio stream information. Each audio stream * corresponds to one "m=" line in SDP session descriptor, and it has * its own RTP/RTCP socket pair. */ typedef struct pjmedia_stream_info { - pjmedia_type type; /**< Media type (audio, video) */ - pjmedia_tp_proto proto; /**< Transport protocol (RTP/AVP, etc.) */ - pjmedia_dir dir; /**< Media direction. */ - pj_sockaddr local_addr; /**< Local RTP address */ - pj_sockaddr rem_addr; /**< Remote RTP address */ - pj_sockaddr rem_rtcp; /**< Optional remote RTCP address. If - sin_family is zero, the RTP address - will be calculated from RTP. */ - pj_bool_t rtcp_mux; /**< Use RTP and RTCP multiplexing. */ -#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) - pj_bool_t rtcp_xr_enabled; - /**< Specify whether RTCP XR is enabled.*/ - pj_uint32_t rtcp_xr_interval; /**< RTCP XR interval. */ - pj_sockaddr rtcp_xr_dest;/** +#include #include #include #include +#include PJ_BEGIN_DECL +/***************************************************************************** + * + * COMMON MEDIA STREAM + * + *****************************************************************************/ + +/* Tracing jitter buffer operations in a stream session to a CSV file. + * The trace will contain JB operation timestamp, frame info, RTP info, and + * the JB state right after the operation. + */ +#define PJMEDIA_STREAM_TRACE_JB 0 + +/* Forward declarations. */ +typedef struct pjmedia_stream_info_common pjmedia_stream_info_common; +typedef struct pjmedia_channel pjmedia_channel; + +/** + * This structure describes media stream. + * A media stream is bidirectional media transmission between two endpoints. + * It consists of two channels, i.e. encoding and decoding channels. + * A media stream corresponds to a single "m=" line in a SDP session + * description. + */ +typedef struct pjmedia_stream_common +{ + pjmedia_endpt *endpt; /**< Media endpoint. */ + pj_grp_lock_t *grp_lock; /**< Group lock. */ + pjmedia_stream_info_common *si; /**< Creation parameter. */ + pjmedia_port port; /**< Port interface. */ + pjmedia_channel *enc; /**< Encoding channel. */ + pjmedia_channel *dec; /**< Decoding channel. */ + pj_pool_t *own_pool; /**< Only created if not given */ + + pjmedia_dir dir; /**< Stream direction. */ + void *user_data; /**< User data. */ + pj_str_t name; /**< Stream name */ + pj_str_t cname; /**< SDES CNAME */ + + pjmedia_transport *transport; /**< Stream transport. */ + + pj_int16_t *enc_buf; /**< Encoding buffer, when enc's + ptime is different than dec. + Otherwise it's NULL. */ + + unsigned frame_size; /**< Size of encoded base frame.*/ + + pj_mutex_t *jb_mutex; + pjmedia_jbuf *jb; /**< Jitter buffer. */ + char jb_last_frm; /**< Last frame type from jb */ + unsigned jb_last_frm_cnt;/**< Last JB frame type counter*/ + + pjmedia_rtcp_session rtcp; /**< RTCP for incoming RTP. */ + + pj_uint32_t rtcp_last_tx; /**< RTCP tx time in timestamp */ + pj_timestamp rtcp_fb_last_tx;/**< Last RTCP-FB tx time. */ + pj_uint32_t rtcp_interval; /**< Interval, in timestamp. */ + pj_bool_t initial_rr; /**< Initial RTCP RR sent */ + pj_bool_t rtcp_sdes_bye_disabled;/**< Send RTCP SDES/BYE?*/ + void *out_rtcp_pkt; /**< Outgoing RTCP packet. */ + unsigned out_rtcp_pkt_size; + /**< Outgoing RTCP packet size. */ + +#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) + pj_uint32_t rtcp_xr_last_tx; /**< RTCP XR tx time + in timestamp. */ + pj_uint32_t rtcp_xr_interval; /**< Interval, in timestamp. */ + pj_sockaddr rtcp_xr_dest; /**< Additional remote RTCP XR + dest. If sin_family is + zero, it will be ignored*/ + unsigned rtcp_xr_dest_len; /**< Length of RTCP XR dest + address */ +#endif + +#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 + pj_bool_t use_ka; /**< Stream keep-alive with non- + codec-VAD mechanism is + enabled? */ + unsigned ka_interval; /**< The keepalive sending + interval */ + pj_time_val last_frm_ts_sent; /**< Time of last sending + packet */ + unsigned start_ka_count; /**< The number of keep-alive + to be sent after it is + created */ + unsigned start_ka_interval;/**< The keepalive sending + interval after the stream + is created */ + pj_timestamp last_start_ka_tx; /**< Timestamp of the last + keepalive sent */ +#endif + + pj_sockaddr rem_rtp_addr; /**< Remote RTP address */ + unsigned rem_rtp_flag; /**< Indicator flag about + packet from this addr. + 0=no pkt, 1=good ssrc, + 2=bad ssrc pkts */ + pj_sockaddr rtp_src_addr; /**< Actual packet src addr. */ + unsigned rtp_src_cnt; /**< How many pkt from + this addr. */ + +#if defined(PJMEDIA_STREAM_TRACE_JB) && PJMEDIA_STREAM_TRACE_JB != 0 + pj_oshandle_t trace_jb_fd; /**< Jitter tracing file handle.*/ + char *trace_jb_buf; /**< Jitter tracing buffer. */ +#endif + + pj_uint32_t rtp_rx_last_ts; /**< Last received RTP + timestamp */ + pj_uint32_t rtp_tx_err_cnt; /**< The number of RTP + send() error */ + pj_uint32_t rtcp_tx_err_cnt; /**< The number of RTCP + send() error */ + + /* RTCP Feedback */ + pj_bool_t send_rtcp_fb_nack; /**< Send NACK? */ + int pending_rtcp_fb_nack; /**< Any pending NACK? */ + pjmedia_rtcp_fb_nack rtcp_fb_nack; /**< TX NACK state. */ + int rtcp_fb_nack_cap_idx; /**< RX NACK cap idx. */ +} pjmedia_stream_common; + + +/** + * Media channel. + * Media channel is unidirectional flow of media from sender to + * receiver. + */ +typedef struct pjmedia_channel +{ + pjmedia_stream_common *stream; /**< Parent stream. */ + pjmedia_dir dir; /**< Channel direction. */ + pjmedia_port port; /**< Port interface. */ + unsigned pt; /**< Payload type. */ + pj_bool_t paused; /**< Paused?. */ + void *buf; /**< Output buffer. */ + unsigned buf_size; /**< Size of output buffer. */ + pjmedia_rtp_session rtp; /**< RTP session. */ +} pjmedia_channel; + + /** * This structure describes rtp/rtcp session information of the media stream. */ @@ -54,6 +194,89 @@ typedef struct pjmedia_stream_rtp_sess_info } pjmedia_stream_rtp_sess_info; + +/** + * Get the stream statistics. See also + * #pjmedia_stream_get_stat_jbuf() + * + * @param stream The media stream. + * @param stat Media stream statistics. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_stream_common_get_stat( const pjmedia_stream_common *stream, + pjmedia_rtcp_stat *stat); + + +/** + * Reset the stream statistics. + * + * @param stream The media stream. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_stream_common_reset_stat(pjmedia_stream_common *stream); + + +/** + * Send RTCP SDES for the media stream. + * + * @param stream The media stream. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_stream_common_send_rtcp_sdes( pjmedia_stream_common *stream ); + +/** + * Send RTCP BYE for the media stream. + * + * @param stream The media stream. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_stream_common_send_rtcp_bye( pjmedia_stream_common *stream ); + + +/** + * Get the RTP session information of the media stream. This function can be + * useful for app with custom media transport to inject/filter some + * outgoing/incoming proprietary packets into normal audio RTP traffics. + * This will return the original pointer to the internal states of the stream, + * and generally it is not advisable for app to modify them. + * + * @param stream The media stream. + * + * @param session_info The stream session info. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_stream_common_get_rtp_session_info(pjmedia_stream_common *stream, + pjmedia_stream_rtp_sess_info *session_info); + + +/* Internal function. */ + +/* Internal: * Send RTCP SDES for the media stream. */ +pj_status_t pjmedia_stream_send_rtcp(pjmedia_stream_common *c_strm, + pj_bool_t with_sdes, + pj_bool_t with_bye, + pj_bool_t with_xr, + pj_bool_t with_fb, + pj_bool_t with_fb_nack, + pj_bool_t with_fb_pli); + + +/***************************************************************************** + * + * COMMON MEDIA STREAM INFORMATION + * + *****************************************************************************/ + #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /** @@ -95,8 +318,114 @@ typedef struct pjmedia_stream_ka_config PJ_DECL(void) pjmedia_stream_ka_config_default(pjmedia_stream_ka_config *cfg); +#define PJ_DECL_STREAM_INFO_KA_MEMBER() \ + pj_bool_t use_ka; /**< Stream keep-alive and NAT hole punch \ + (see #PJMEDIA_STREAM_ENABLE_KA) \ + is enabled? */ \ + pjmedia_stream_ka_config ka_cfg; \ + /**< Stream send kep-alive settings. */ \ + +#else + +#define PJ_DECL_STREAM_INFO_KA_MEMBER() + #endif + +/** + * This structure describes the common media stream information. + */ +#define PJ_DECL_STREAM_INFO_COMMON_MEMBER() \ + pjmedia_type type; /**< Media type (audio, video) */ \ + pjmedia_tp_proto proto; /**< Transport protocol (RTP/AVP, etc.) */ \ + pjmedia_dir dir; /**< Media direction. */ \ + pj_sockaddr local_addr; /**< Local RTP address */ \ + pj_sockaddr rem_addr; /**< Remote RTP address */ \ + pj_sockaddr rem_rtcp; /**< Optional remote RTCP address. If \ + sin_family is zero, the RTP address \ + will be calculated from RTP. */ \ + pj_bool_t rtcp_mux; /**< Use RTP and RTCP multiplexing. */ \ +\ + pj_bool_t rtcp_xr_enabled; \ + /**< Specify whether RTCP XR is enabled.*/ \ + pj_uint32_t rtcp_xr_interval; /**< RTCP XR interval. */ \ + pj_sockaddr rtcp_xr_dest;/** -# define TRACE_JB_INVALID_FD ((pj_oshandle_t)-1) -# define TRACE_JB_OPENED(s) (s->trace_jb_fd != TRACE_JB_INVALID_FD) -#endif +/* Enable/disable trace. */ +#define TRACE_JB PJMEDIA_STREAM_TRACE_JB +/* Optional path/prefix for the CSV filename. */ +#define TRACE_JB_PATH_PREFIX "" #ifndef PJMEDIA_STREAM_SIZE # define PJMEDIA_STREAM_SIZE 4000 @@ -77,20 +70,6 @@ /* Number of send error before repeat the report. */ #define SEND_ERR_COUNT_TO_REPORT 50 -/** - * Media channel. - */ -struct pjmedia_channel -{ - pjmedia_stream *stream; /**< Parent stream. */ - pjmedia_dir dir; /**< Channel direction. */ - unsigned pt; /**< Payload type. */ - pj_bool_t paused; /**< Paused?. */ - unsigned out_pkt_size; /**< Size of output buffer. */ - void *out_pkt; /**< Output buffer. */ - pjmedia_rtp_session rtp; /**< RTP session. */ -}; - struct dtmf { @@ -110,21 +89,10 @@ struct dtmf */ struct pjmedia_stream { - pjmedia_endpt *endpt; /**< Media endpoint. */ - pj_grp_lock_t *grp_lock; /**< Group lock. */ + pjmedia_stream_common base; + pjmedia_codec_mgr *codec_mgr; /**< Codec manager instance. */ pjmedia_stream_info si; /**< Creation parameter. */ - pjmedia_port port; /**< Port interface. */ - pjmedia_channel *enc; /**< Encoding channel. */ - pjmedia_channel *dec; /**< Decoding channel. */ - - pj_pool_t *own_pool; /**< Only created if not given */ - - pjmedia_dir dir; /**< Stream direction. */ - void *user_data; /**< User data. */ - pj_str_t cname; /**< SDES CNAME */ - - pjmedia_transport *transport; /**< Stream transport. */ pjmedia_codec *codec; /**< Codec instance being used. */ pjmedia_codec_param codec_param; /**< Codec param. */ @@ -162,21 +130,8 @@ struct pjmedia_stream pj_uint32_t ts_vad_disabled;/**< TS when VAD was disabled. */ pj_uint32_t tx_duration; /**< TX duration in timestamp. */ - pj_mutex_t *jb_mutex; - pjmedia_jbuf *jb; /**< Jitter buffer. */ - char jb_last_frm; /**< Last frame type from jb */ - unsigned jb_last_frm_cnt;/**< Last JB frame type counter*/ unsigned soft_start_cnt;/**< Stream soft start counter */ - pjmedia_rtcp_session rtcp; /**< RTCP for incoming RTP. */ - - pj_uint32_t rtcp_last_tx; /**< RTCP tx time in timestamp */ - pj_uint32_t rtcp_interval; /**< Interval, in timestamp. */ - pj_bool_t initial_rr; /**< Initial RTCP RR sent */ - pj_bool_t rtcp_sdes_bye_disabled;/**< Send RTCP SDES/BYE?*/ - void *out_rtcp_pkt; /**< Outgoing RTCP packet. */ - unsigned out_rtcp_pkt_size; - /**< Outgoing RTCP packet size. */ pj_int16_t *zero_frame; /**< Zero frame buffer. */ /* RFC 2833 DTMF transmission queue: */ @@ -224,59 +179,6 @@ struct pjmedia_stream checking */ #endif -#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) - pj_uint32_t rtcp_xr_last_tx; /**< RTCP XR tx time - in timestamp. */ - pj_uint32_t rtcp_xr_interval; /**< Interval, in timestamp. */ - pj_sockaddr rtcp_xr_dest; /**< Additional remote RTCP XR - dest. If sin_family is - zero, it will be ignored*/ - unsigned rtcp_xr_dest_len; /**< Length of RTCP XR dest - address */ -#endif - -#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 - pj_bool_t use_ka; /**< Stream keep-alive with non- - codec-VAD mechanism is - enabled? */ - unsigned ka_interval; /**< The keepalive sending - interval */ - pj_time_val last_frm_ts_sent; /**< Time of last sending - packet */ - unsigned start_ka_count; /**< The number of keep-alive - to be sent after it is - created */ - unsigned start_ka_interval;/**< The keepalive sending - interval after the stream - is created */ -#endif - - pj_sockaddr rem_rtp_addr; /**< Remote RTP address */ - unsigned rem_rtp_flag; /**< Indicator flag about - packet from this addr. - 0=no pkt, 1=good ssrc, - 2=bad ssrc pkts */ - unsigned rtp_src_cnt; /**< How many pkt from - this addr. */ - -#if TRACE_JB - pj_oshandle_t trace_jb_fd; /**< Jitter tracing file handle.*/ - char *trace_jb_buf; /**< Jitter tracing buffer. */ -#endif - - pj_uint32_t rtp_rx_last_ts; /**< Last received RTP - timestamp */ - pj_uint32_t rtp_tx_err_cnt; /**< The number of RTP - send() error */ - pj_uint32_t rtcp_tx_err_cnt; /**< The number of RTCP - send() error */ - - /* RTCP Feedback */ - pj_bool_t send_rtcp_fb_nack; /**< Send NACK? */ - pjmedia_rtcp_fb_nack rtcp_fb_nack; /**< TX NACK state. */ - int rtcp_fb_nack_cap_idx; /**< RX NACK cap idx. */ - - }; @@ -295,225 +197,8 @@ static void on_rx_rtcp( void *data, void *pkt, pj_ssize_t bytes_read); -static pj_status_t send_rtcp(pjmedia_stream *stream, - pj_bool_t with_sdes, - pj_bool_t with_bye, - pj_bool_t with_xr, - pj_bool_t with_fb); - -static void stream_on_destroy(void *arg); - -#if TRACE_JB - -PJ_INLINE(int) trace_jb_print_timestamp(char **buf, pj_ssize_t len) -{ - pj_time_val now; - pj_parsed_time ptime; - char *p = *buf; - - if (len < 14) - return -1; - - pj_gettimeofday(&now); - pj_time_decode(&now, &ptime); - p += pj_utoa_pad(ptime.hour, p, 2, '0'); - *p++ = ':'; - p += pj_utoa_pad(ptime.min, p, 2, '0'); - *p++ = ':'; - p += pj_utoa_pad(ptime.sec, p, 2, '0'); - *p++ = '.'; - p += pj_utoa_pad(ptime.msec, p, 3, '0'); - *p++ = ','; - - *buf = p; - - return 0; -} - -PJ_INLINE(int) trace_jb_print_state(pjmedia_stream *stream, - char **buf, pj_ssize_t len) -{ - char *p = *buf; - char *endp = *buf + len; - pjmedia_jb_state state; - - pjmedia_jbuf_get_state(stream->jb, &state); - - len = pj_ansi_snprintf(p, endp-p, "%d, %d, %d", - state.size, state.burst, state.prefetch); - if ((len < 0) || (len >= endp-p)) - return -1; - - p += len; - *buf = p; - return 0; -} - -static void trace_jb_get(pjmedia_stream *stream, pjmedia_jb_frame_type ft, - pj_size_t fsize) -{ - char *p = stream->trace_jb_buf; - char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE; - pj_ssize_t len = 0; - const char* ft_st; - - if (!TRACE_JB_OPENED(stream)) - return; - - /* Print timestamp. */ - if (trace_jb_print_timestamp(&p, endp-p)) - goto on_insuff_buffer; - - /* Print frame type and size */ - switch(ft) { - case PJMEDIA_JB_MISSING_FRAME: - ft_st = "missing"; - break; - case PJMEDIA_JB_NORMAL_FRAME: - ft_st = "normal"; - break; - case PJMEDIA_JB_ZERO_PREFETCH_FRAME: - ft_st = "prefetch"; - break; - case PJMEDIA_JB_ZERO_EMPTY_FRAME: - ft_st = "empty"; - break; - default: - ft_st = "unknown"; - break; - } - - /* Print operation, size, frame count, frame type */ - len = pj_ansi_snprintf(p, endp-p, "GET,%d,1,%s,,,,", fsize, ft_st); - if ((len < 0) || (len >= endp-p)) - goto on_insuff_buffer; - p += len; - - /* Print JB state */ - if (trace_jb_print_state(stream, &p, endp-p)) - goto on_insuff_buffer; - - /* Print end of line */ - if (endp-p < 2) - goto on_insuff_buffer; - *p++ = '\n'; - - /* Write and flush */ - len = p - stream->trace_jb_buf; - pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); - pj_file_flush(stream->trace_jb_fd); - return; - -on_insuff_buffer: - pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!"); -} - -static void trace_jb_put(pjmedia_stream *stream, const pjmedia_rtp_hdr *hdr, - unsigned payloadlen, unsigned frame_cnt) -{ - char *p = stream->trace_jb_buf; - char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE; - pj_ssize_t len = 0; - - if (!TRACE_JB_OPENED(stream)) - return; - - /* Print timestamp. */ - if (trace_jb_print_timestamp(&p, endp-p)) - goto on_insuff_buffer; - - /* Print operation, size, frame count, RTP info */ - len = pj_ansi_snprintf(p, endp-p, - "PUT,%d,%d,,%d,%d,%d,", - payloadlen, frame_cnt, - pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), hdr->m); - if ((len < 0) || (len >= endp-p)) - goto on_insuff_buffer; - p += len; - - /* Print JB state */ - if (trace_jb_print_state(stream, &p, endp-p)) - goto on_insuff_buffer; - - /* Print end of line */ - if (endp-p < 2) - goto on_insuff_buffer; - *p++ = '\n'; - - /* Write and flush */ - len = p - stream->trace_jb_buf; - pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); - pj_file_flush(stream->trace_jb_fd); - return; - -on_insuff_buffer: - pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!"); -} - -#endif /* TRACE_JB */ - -#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0 -/* - * Send keep-alive packet using non-codec frame. - */ -static void send_keep_alive_packet(pjmedia_stream *stream) -{ -#if PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_EMPTY_RTP - - /* Keep-alive packet is empty RTP */ - pj_status_t status; - void *pkt; - int pkt_len; - - TRC_((stream->port.info.name.ptr, - "Sending keep-alive (RTCP and empty RTP)")); - - /* Send RTP */ - status = pjmedia_rtp_encode_rtp( &stream->enc->rtp, - stream->enc->pt, 0, - 1, - 0, - (const void**)&pkt, - &pkt_len); - pj_assert(status == PJ_SUCCESS); - - pj_memcpy(stream->enc->out_pkt, pkt, pkt_len); - pjmedia_transport_send_rtp(stream->transport, stream->enc->out_pkt, - pkt_len); - - /* Send RTCP */ - send_rtcp(stream, PJ_TRUE, PJ_FALSE, PJ_FALSE, PJ_FALSE); - - /* Update stats in case the stream is paused */ - stream->rtcp.stat.rtp_tx_last_seq = pj_ntohs(stream->enc->rtp.out_hdr.seq); - -#elif PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_USER - - /* Keep-alive packet is defined in PJMEDIA_STREAM_KA_USER_PKT */ - int pkt_len; - const pj_str_t str_ka = PJMEDIA_STREAM_KA_USER_PKT; - - TRC_((stream->port.info.name.ptr, - "Sending keep-alive (custom RTP/RTCP packets)")); - - /* Send to RTP port */ - pj_memcpy(stream->enc->out_pkt, str_ka.ptr, str_ka.slen); - pkt_len = str_ka.slen; - pjmedia_transport_send_rtp(stream->transport, stream->enc->out_pkt, - pkt_len); - - /* Send to RTCP port */ - pjmedia_transport_send_rtcp(stream->transport, stream->enc->out_pkt, - pkt_len); - -#else - - PJ_UNUSED_ARG(stream); - -#endif -} -#endif /* defined(PJMEDIA_STREAM_ENABLE_KA) */ +#include "stream_imp_common.c" /* * play_callback() @@ -524,7 +209,8 @@ static void send_keep_alive_packet(pjmedia_stream *stream) static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) { pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata; - pjmedia_channel *channel = stream->dec; + pjmedia_stream_common *c_strm = &stream->base; + pjmedia_channel *channel = c_strm->dec; unsigned samples_count, samples_per_frame, samples_required; pj_int16_t *p_out_samp; pj_status_t status; @@ -538,11 +224,11 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) if (stream->soft_start_cnt) { if (stream->soft_start_cnt == PJMEDIA_STREAM_SOFT_START) { - PJ_LOG(4,(stream->port.info.name.ptr, + PJ_LOG(4,(c_strm->port.info.name.ptr, "Resetting jitter buffer in stream playback start")); - pj_mutex_lock( stream->jb_mutex ); - pjmedia_jbuf_reset(stream->jb); - pj_mutex_unlock( stream->jb_mutex ); + pj_mutex_lock( c_strm->jb_mutex ); + pjmedia_jbuf_reset(c_strm->jb); + pj_mutex_unlock( c_strm->jb_mutex ); } --stream->soft_start_cnt; frame->type = PJMEDIA_FRAME_TYPE_NONE; @@ -554,9 +240,9 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) */ /* Lock jitter buffer mutex first */ - pj_mutex_lock( stream->jb_mutex ); + pj_mutex_lock( c_strm->jb_mutex ); - samples_required = PJMEDIA_PIA_SPF(&stream->port.info); + samples_required = PJMEDIA_PIA_SPF(&c_strm->port.info); samples_per_frame = stream->dec_ptime * stream->codec_param.info.clock_rate * stream->codec_param.info.channel_cnt / @@ -566,7 +252,7 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) for (samples_count=0; samples_count < samples_required;) { char frame_type; - pj_size_t frame_size = channel->out_pkt_size; + pj_size_t frame_size = channel->buf_size; pj_uint32_t bit_info; if (stream->dec_buf && stream->dec_buf_pos < stream->dec_buf_count) { @@ -584,11 +270,11 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) } /* Get frame from jitter buffer. */ - pjmedia_jbuf_get_frame2(stream->jb, channel->out_pkt, &frame_size, + pjmedia_jbuf_get_frame2(c_strm->jb, channel->buf, &frame_size, &frame_type, &bit_info); #if TRACE_JB - trace_jb_get(stream, frame_type, frame_size); + trace_jb_get(c_strm, frame_type, frame_size); #endif if (frame_type == PJMEDIA_JB_MISSING_FRAME) { @@ -618,15 +304,15 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) samples_required - samples_count); } - if (frame_type != stream->jb_last_frm) { + if (frame_type != c_strm->jb_last_frm) { /* Report changing frame type event */ - PJ_LOG(5,(stream->port.info.name.ptr, "Frame lost%s!", + PJ_LOG(5,(c_strm->port.info.name.ptr, "Frame lost%s!", (status == PJ_SUCCESS? ", recovered":""))); - stream->jb_last_frm = frame_type; - stream->jb_last_frm_cnt = 1; + c_strm->jb_last_frm = frame_type; + c_strm->jb_last_frm_cnt = 1; } else { - stream->jb_last_frm_cnt++; + c_strm->jb_last_frm_cnt++; } samples_count += samples_per_frame; @@ -640,7 +326,7 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) */ //Using this "if" will only invoke PLC for the first packet //lost and not the subsequent ones. - //if (frame_type != stream->jb_last_frm) { + //if (frame_type != c_strm->jb_last_frm) { if (1) { /* Activate PLC to smoothen the missing frame */ if (stream->codec->op->recover && @@ -674,19 +360,19 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) samples_count = samples_required; } - if (stream->jb_last_frm != frame_type) { + if (c_strm->jb_last_frm != frame_type) { pjmedia_jb_state jb_state; /* Report changing frame type event */ - pjmedia_jbuf_get_state(stream->jb, &jb_state); - PJ_LOG(5,(stream->port.info.name.ptr, + pjmedia_jbuf_get_state(c_strm->jb, &jb_state); + PJ_LOG(5,(c_strm->port.info.name.ptr, "Jitter buffer empty (prefetch=%d)%s", jb_state.prefetch, with_plc)); - stream->jb_last_frm = frame_type; - stream->jb_last_frm_cnt = 1; + c_strm->jb_last_frm = frame_type; + c_strm->jb_last_frm_cnt = 1; } else { - stream->jb_last_frm_cnt++; + c_strm->jb_last_frm_cnt++; } break; @@ -728,19 +414,19 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) samples_count = samples_required; } - if (stream->jb_last_frm != frame_type) { + if (c_strm->jb_last_frm != frame_type) { pjmedia_jb_state jb_state; /* Report changing frame type event */ - pjmedia_jbuf_get_state(stream->jb, &jb_state); - PJ_LOG(5,(stream->port.info.name.ptr, + pjmedia_jbuf_get_state(c_strm->jb, &jb_state); + PJ_LOG(5,(c_strm->port.info.name.ptr, "Jitter buffer is bufferring (prefetch=%d)%s", jb_state.prefetch, with_plc)); - stream->jb_last_frm = frame_type; - stream->jb_last_frm_cnt = 1; + c_strm->jb_last_frm = frame_type; + c_strm->jb_last_frm_cnt = 1; } else { - stream->jb_last_frm_cnt++; + c_strm->jb_last_frm_cnt++; } break; @@ -752,7 +438,7 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) stream->plc_cnt = 0; /* Decode */ - frame_in.buf = channel->out_pkt; + frame_in.buf = channel->buf; frame_in.size = frame_size; frame_in.bit_info = bit_info; frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO; /* ignored */ @@ -789,17 +475,17 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) sizeof(pj_int16_t); } - if (stream->jb_last_frm != frame_type) { + if (c_strm->jb_last_frm != frame_type) { /* Report changing frame type event */ - PJ_LOG(5,(stream->port.info.name.ptr, + PJ_LOG(5,(c_strm->port.info.name.ptr, "Jitter buffer starts returning normal frames " "(after %d empty/lost)", - stream->jb_last_frm_cnt)); + c_strm->jb_last_frm_cnt)); - stream->jb_last_frm = frame_type; - stream->jb_last_frm_cnt = 1; + c_strm->jb_last_frm = frame_type; + c_strm->jb_last_frm_cnt = 1; } else { - stream->jb_last_frm_cnt++; + c_strm->jb_last_frm_cnt++; } if (!use_dec_buf) samples_count += samples_per_frame; @@ -808,7 +494,7 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) /* Unlock jitter buffer mutex. */ - pj_mutex_unlock( stream->jb_mutex ); + pj_mutex_unlock( c_strm->jb_mutex ); /* Return PJMEDIA_FRAME_TYPE_NONE if we have no frames at all * (it can happen when jitter buffer returns PJMEDIA_JB_ZERO_EMPTY_FRAME). @@ -832,7 +518,8 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) { pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata; - pjmedia_channel *channel = stream->dec; + pjmedia_stream_common *c_strm = &stream->base; + pjmedia_channel *channel = c_strm->dec; pjmedia_frame_ext *f = (pjmedia_frame_ext*)frame; unsigned samples_per_frame, samples_required; pj_status_t status; @@ -847,7 +534,7 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) * until we have enough frames according to codec's ptime. */ - samples_required = PJMEDIA_PIA_SPF(&stream->port.info); + samples_required = PJMEDIA_PIA_SPF(&c_strm->port.info); samples_per_frame = stream->codec_param.info.frm_ptime * stream->codec_param.info.clock_rate * stream->codec_param.info.channel_cnt / @@ -859,29 +546,29 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) while (f->samples_cnt < samples_required) { char frame_type; - pj_size_t frame_size = channel->out_pkt_size; + pj_size_t frame_size = channel->buf_size; pj_uint32_t bit_info; /* Lock jitter buffer mutex first */ - pj_mutex_lock( stream->jb_mutex ); + pj_mutex_lock( c_strm->jb_mutex ); /* Get frame from jitter buffer. */ - pjmedia_jbuf_get_frame2(stream->jb, channel->out_pkt, &frame_size, + pjmedia_jbuf_get_frame2(c_strm->jb, channel->buf, &frame_size, &frame_type, &bit_info); #if TRACE_JB - trace_jb_get(stream, frame_type, frame_size); + trace_jb_get(c_strm, frame_type, frame_size); #endif /* Unlock jitter buffer mutex. */ - pj_mutex_unlock( stream->jb_mutex ); + pj_mutex_unlock( c_strm->jb_mutex ); if (frame_type == PJMEDIA_JB_NORMAL_FRAME) { /* Got "NORMAL" frame from jitter buffer */ pjmedia_frame frame_in; /* Decode */ - frame_in.buf = channel->out_pkt; + frame_in.buf = channel->buf; frame_in.size = frame_size; frame_in.bit_info = bit_info; frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO; @@ -895,17 +582,17 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) (pj_uint16_t)samples_per_frame); } - if (stream->jb_last_frm != frame_type) { + if (c_strm->jb_last_frm != frame_type) { /* Report changing frame type event */ - PJ_LOG(5,(stream->port.info.name.ptr, + PJ_LOG(5,(c_strm->port.info.name.ptr, "Jitter buffer starts returning normal frames " "(after %d empty/lost)", - stream->jb_last_frm_cnt)); + c_strm->jb_last_frm_cnt)); - stream->jb_last_frm = frame_type; - stream->jb_last_frm_cnt = 1; + c_strm->jb_last_frm = frame_type; + c_strm->jb_last_frm_cnt = 1; } else { - stream->jb_last_frm_cnt++; + c_strm->jb_last_frm_cnt++; } } else { @@ -923,48 +610,48 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) } if (frame_type == PJMEDIA_JB_MISSING_FRAME) { - if (frame_type != stream->jb_last_frm) { + if (frame_type != c_strm->jb_last_frm) { /* Report changing frame type event */ - PJ_LOG(5,(stream->port.info.name.ptr, "Frame lost!")); + PJ_LOG(5,(c_strm->port.info.name.ptr, "Frame lost!")); - stream->jb_last_frm = frame_type; - stream->jb_last_frm_cnt = 1; + c_strm->jb_last_frm = frame_type; + c_strm->jb_last_frm_cnt = 1; } else { - stream->jb_last_frm_cnt++; + c_strm->jb_last_frm_cnt++; } } else if (frame_type == PJMEDIA_JB_ZERO_EMPTY_FRAME) { - if (frame_type != stream->jb_last_frm) { + if (frame_type != c_strm->jb_last_frm) { pjmedia_jb_state jb_state; /* Report changing frame type event */ - pjmedia_jbuf_get_state(stream->jb, &jb_state); - PJ_LOG(5,(stream->port.info.name.ptr, + pjmedia_jbuf_get_state(c_strm->jb, &jb_state); + PJ_LOG(5,(c_strm->port.info.name.ptr, "Jitter buffer empty (prefetch=%d)", jb_state.prefetch)); - stream->jb_last_frm = frame_type; - stream->jb_last_frm_cnt = 1; + c_strm->jb_last_frm = frame_type; + c_strm->jb_last_frm_cnt = 1; } else { - stream->jb_last_frm_cnt++; + c_strm->jb_last_frm_cnt++; } } else { /* It can only be PJMEDIA_JB_ZERO_PREFETCH frame */ pj_assert(frame_type == PJMEDIA_JB_ZERO_PREFETCH_FRAME); - if (stream->jb_last_frm != frame_type) { + if (c_strm->jb_last_frm != frame_type) { pjmedia_jb_state jb_state; /* Report changing frame type event */ - pjmedia_jbuf_get_state(stream->jb, &jb_state); - PJ_LOG(5,(stream->port.info.name.ptr, + pjmedia_jbuf_get_state(c_strm->jb, &jb_state); + PJ_LOG(5,(c_strm->port.info.name.ptr, "Jitter buffer is bufferring (prefetch=%d)", jb_state.prefetch)); - stream->jb_last_frm = frame_type; - stream->jb_last_frm_cnt = 1; + c_strm->jb_last_frm = frame_type; + c_strm->jb_last_frm_cnt = 1; } else { - stream->jb_last_frm_cnt++; + c_strm->jb_last_frm_cnt++; } } } @@ -981,6 +668,7 @@ static void create_dtmf_payload(pjmedia_stream *stream, struct pjmedia_frame *frame_out, int forced_last, int *first, int *last) { + pjmedia_stream_common *c_strm = &stream->base; pjmedia_rtp_dtmf_event *event; struct dtmf *digit = &stream->tx_dtmf_buf[0]; unsigned duration = 0; @@ -1010,7 +698,7 @@ static void create_dtmf_payload(pjmedia_stream *stream, event = (pjmedia_rtp_dtmf_event*) frame_out->buf; if (digit->duration == 0) { - PJ_LOG(4,(stream->port.info.name.ptr, "Sending DTMF digit id %c", + PJ_LOG(4,(c_strm->port.info.name.ptr, "Sending DTMF digit id %c", digitmap[digit->event])); *first = 1; } @@ -1034,13 +722,13 @@ static void create_dtmf_payload(pjmedia_stream *stream, *last = 1; /* Prepare next digit. */ - pj_mutex_lock(stream->jb_mutex); + pj_mutex_lock(c_strm->jb_mutex); pj_array_erase(stream->tx_dtmf_buf, sizeof(stream->tx_dtmf_buf[0]), stream->tx_dtmf_count, 0); --stream->tx_dtmf_count; - pj_mutex_unlock(stream->jb_mutex); + pj_mutex_unlock(c_strm->jb_mutex); } } @@ -1048,197 +736,6 @@ static void create_dtmf_payload(pjmedia_stream *stream, } -static pj_status_t build_rtcp_fb(pjmedia_stream *stream, void *buf, - pj_size_t *length) -{ - pj_status_t status; - - /* Generic NACK */ - if (stream->send_rtcp_fb_nack && stream->rtcp_fb_nack.pid >= 0) - { - status = pjmedia_rtcp_fb_build_nack(&stream->rtcp, buf, length, 1, - &stream->rtcp_fb_nack); - if (status != PJ_SUCCESS) - return status; - - /* Reset Packet ID */ - stream->rtcp_fb_nack.pid = -1; - } - - return PJ_SUCCESS; -} - - -/** - * Publish transport error event. - */ -static void publish_tp_event(pjmedia_event_type event_type, - pj_status_t status, - pj_bool_t is_rtp, - pjmedia_dir dir, - pjmedia_stream *stream) -{ - pjmedia_event ev; - pj_timestamp ts_now; - - pj_get_timestamp(&ts_now); - pj_bzero(&ev.data.med_tp_err, sizeof(ev.data.med_tp_err)); - - /* Publish event. */ - pjmedia_event_init(&ev, event_type, - &ts_now, stream); - ev.data.med_tp_err.type = PJMEDIA_TYPE_AUDIO; - ev.data.med_tp_err.is_rtp = is_rtp; - ev.data.med_tp_err.dir = dir; - ev.data.med_tp_err.status = status; - - pjmedia_event_publish(NULL, stream, &ev, 0); -} - - -static pj_status_t send_rtcp(pjmedia_stream *stream, - pj_bool_t with_sdes, - pj_bool_t with_bye, - pj_bool_t with_xr, - pj_bool_t with_fb) -{ - void *sr_rr_pkt; - pj_uint8_t *pkt; - int len, max_len; - pj_status_t status; - - /* We need to prevent data race since there is only a single instance - * of rtcp packet buffer. And to avoid deadlock with media transport, - * we use the transport's group lock. - */ - if (stream->transport->grp_lock) - pj_grp_lock_acquire(stream->transport->grp_lock); - - /* Build RTCP RR/SR packet */ - pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len); - -#if !defined(PJMEDIA_HAS_RTCP_XR) || (PJMEDIA_HAS_RTCP_XR == 0) - with_xr = PJ_FALSE; -#endif - - if (with_sdes || with_bye || with_xr || with_fb) { - pkt = (pj_uint8_t*) stream->out_rtcp_pkt; - pj_memcpy(pkt, sr_rr_pkt, len); - max_len = stream->out_rtcp_pkt_size; - } else { - pkt = (pj_uint8_t*)sr_rr_pkt; - max_len = len; - } - - /* RTCP FB must be sent in compound (i.e: with RR/SR and SDES) */ - if (with_fb) - with_sdes = PJ_TRUE; - - /* Build RTCP SDES packet */ - if (with_sdes) { - pjmedia_rtcp_sdes sdes; - pj_size_t sdes_len; - - pj_bzero(&sdes, sizeof(sdes)); - sdes.cname = stream->cname; - sdes_len = max_len - len; - status = pjmedia_rtcp_build_rtcp_sdes(&stream->rtcp, pkt+len, - &sdes_len, &sdes); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->port.info.name.ptr, status, - "Error generating RTCP SDES")); - } else { - len += (int)sdes_len; - } - } - - if (with_fb) { - pj_size_t fb_len = max_len - len; - status = build_rtcp_fb(stream, pkt+len, &fb_len); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->port.info.name.ptr, status, - "Error generating RTCP FB")); - } else { - len += (int)fb_len; - } - } - - /* Build RTCP XR packet */ -#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) - if (with_xr) { - int i; - pjmedia_jb_state jb_state; - void *xr_pkt; - int xr_len; - - /* Update RTCP XR with current JB states */ - pjmedia_jbuf_get_state(stream->jb, &jb_state); - - i = jb_state.avg_delay; - status = pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, - PJMEDIA_RTCP_XR_INFO_JB_NOM, i); - pj_assert(status == PJ_SUCCESS); - - i = jb_state.max_delay; - status = pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, - PJMEDIA_RTCP_XR_INFO_JB_MAX, i); - pj_assert(status == PJ_SUCCESS); - - pjmedia_rtcp_build_rtcp_xr(&stream->rtcp.xr_session, 0, - &xr_pkt, &xr_len); - - if (xr_len + len <= max_len) { - pj_memcpy(pkt+len, xr_pkt, xr_len); - len += xr_len; - - /* Send the RTCP XR to third-party destination if specified */ - if (stream->rtcp_xr_dest_len) { - pjmedia_transport_send_rtcp2(stream->transport, - &stream->rtcp_xr_dest, - stream->rtcp_xr_dest_len, - xr_pkt, xr_len); - } - - } else { - PJ_PERROR(4,(stream->port.info.name.ptr, PJ_ETOOBIG, - "Error generating RTCP-XR")); - } - } -#endif - - /* Build RTCP BYE packet */ - if (with_bye) { - pj_size_t bye_len; - - bye_len = max_len - len; - status = pjmedia_rtcp_build_rtcp_bye(&stream->rtcp, pkt+len, - &bye_len, NULL); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->port.info.name.ptr, status, - "Error generating RTCP BYE")); - } else { - len += (int)bye_len; - } - } - - /* Send! */ - status = pjmedia_transport_send_rtcp(stream->transport, pkt, len); - if (status != PJ_SUCCESS) { - if (stream->rtcp_tx_err_cnt++ == 0) { - LOGERR_((stream->port.info.name.ptr, status, - "Error sending RTCP")); - } - if (stream->rtcp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) { - stream->rtcp_tx_err_cnt = 0; - } - } - - if (stream->transport->grp_lock) - pj_grp_lock_release(stream->transport->grp_lock); - - return status; -} - /** * check_tx_rtcp() * @@ -1247,38 +744,40 @@ static pj_status_t send_rtcp(pjmedia_stream *stream, */ static void check_tx_rtcp(pjmedia_stream *stream, pj_uint32_t timestamp) { + pjmedia_stream_common *c_strm = &stream->base; + /* Note that timestamp may represent local or remote timestamp, * depending on whether this function is called from put_frame() * or get_frame(). */ - if (stream->rtcp_last_tx == 0) { + if (c_strm->rtcp_last_tx == 0) { - stream->rtcp_last_tx = timestamp; + c_strm->rtcp_last_tx = timestamp; - } else if (timestamp - stream->rtcp_last_tx >= stream->rtcp_interval) { + } else if (timestamp - c_strm->rtcp_last_tx >= c_strm->rtcp_interval) { pj_bool_t with_xr = PJ_FALSE; pj_status_t status; #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) - if (stream->rtcp.xr_enabled) { - if (stream->rtcp_xr_last_tx == 0) { - stream->rtcp_xr_last_tx = timestamp; - } else if (timestamp - stream->rtcp_xr_last_tx >= - stream->rtcp_xr_interval) + if (c_strm->rtcp.xr_enabled) { + if (c_strm->rtcp_xr_last_tx == 0) { + c_strm->rtcp_xr_last_tx = timestamp; + } else if (timestamp - c_strm->rtcp_xr_last_tx >= + c_strm->rtcp_xr_interval) { with_xr = PJ_TRUE; /* Update last tx RTCP XR */ - stream->rtcp_xr_last_tx = timestamp; + c_strm->rtcp_xr_last_tx = timestamp; } } #endif - status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, PJ_FALSE, - with_xr, PJ_FALSE); + status = send_rtcp(c_strm, !c_strm->rtcp_sdes_bye_disabled, PJ_FALSE, + with_xr, PJ_FALSE, PJ_FALSE, PJ_FALSE); if (status == PJ_SUCCESS) { - stream->rtcp_last_tx = timestamp; + c_strm->rtcp_last_tx = timestamp; } } } @@ -1291,6 +790,8 @@ static void check_tx_rtcp(pjmedia_stream *stream, pj_uint32_t timestamp) static void rebuffer(pjmedia_stream *stream, pjmedia_frame *frame) { + pjmedia_stream_common *c_strm = &stream->base; + /* How many samples are needed */ unsigned count; @@ -1301,8 +802,8 @@ static void rebuffer(pjmedia_stream *stream, /* Remove used frame from the buffer. */ if (stream->enc_buf_pos) { if (stream->enc_buf_count) { - pj_memmove(stream->enc_buf, - stream->enc_buf + stream->enc_buf_pos, + pj_memmove(c_strm->enc_buf, + c_strm->enc_buf + stream->enc_buf_pos, (stream->enc_buf_count << 1)); } stream->enc_buf_pos = 0; @@ -1316,17 +817,17 @@ static void rebuffer(pjmedia_stream *stream, if (frame->size) { /* Handle case when there is no port transmitting to this port */ if (frame->buf) { - pj_memcpy(stream->enc_buf + stream->enc_buf_count, + pj_memcpy(c_strm->enc_buf + stream->enc_buf_count, frame->buf, frame->size); } else { - pj_bzero(stream->enc_buf + stream->enc_buf_count, frame->size); + pj_bzero(c_strm->enc_buf + stream->enc_buf_count, frame->size); } stream->enc_buf_count += ((unsigned)frame->size >> 1); } /* How many samples are needed */ count = stream->codec_param.info.enc_ptime * - PJMEDIA_PIA_SRATE(&stream->port.info) / + PJMEDIA_PIA_SRATE(&c_strm->port.info) / stream->codec_param.info.enc_ptime_denum / 1000; @@ -1334,7 +835,7 @@ static void rebuffer(pjmedia_stream *stream, if (stream->enc_buf_count >= count) { frame->type = PJMEDIA_FRAME_TYPE_AUDIO; - frame->buf = stream->enc_buf; + frame->buf = c_strm->enc_buf; frame->size = (count << 1); stream->enc_buf_pos = count; @@ -1354,7 +855,8 @@ static pj_status_t put_frame_imp( pjmedia_port *port, pjmedia_frame *frame ) { pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata; - pjmedia_channel *channel = stream->enc; + pjmedia_stream_common *c_strm = &stream->base; + pjmedia_channel *channel = c_strm->enc; pj_status_t status = 0; pjmedia_frame frame_out; unsigned ts_len, rtp_ts_len; @@ -1367,26 +869,26 @@ static pj_status_t put_frame_imp( pjmedia_port *port, /* If the interval since last sending packet is greater than * PJMEDIA_STREAM_KA_INTERVAL, send keep-alive packet. */ - if (stream->use_ka) + if (c_strm->use_ka) { pj_uint32_t dtx_duration, ka_interval; pj_time_val now, tmp; pj_gettimeofday(&now); tmp = now; - PJ_TIME_VAL_SUB(tmp, stream->last_frm_ts_sent); + PJ_TIME_VAL_SUB(tmp, c_strm->last_frm_ts_sent); dtx_duration = PJ_TIME_VAL_MSEC(tmp); - if (stream->start_ka_count) { - ka_interval = stream->start_ka_interval; + if (c_strm->start_ka_count) { + ka_interval = c_strm->start_ka_interval; } else { - ka_interval = stream->ka_interval * 1000; + ka_interval = c_strm->ka_interval * 1000; } if (dtx_duration > ka_interval) { - send_keep_alive_packet(stream); - stream->last_frm_ts_sent = now; + send_keep_alive_packet(c_strm); + c_strm->last_frm_ts_sent = now; - if (stream->start_ka_count) - stream->start_ka_count--; + if (c_strm->start_ka_count) + c_strm->start_ka_count--; } } #endif @@ -1396,8 +898,8 @@ static pj_status_t put_frame_imp( pjmedia_port *port, ts_len = ((unsigned)frame->size >> 1) / stream->codec_param.info.channel_cnt; else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) - ts_len = PJMEDIA_PIA_SPF(&stream->port.info) / - PJMEDIA_PIA_CCNT(&stream->port.info); + ts_len = PJMEDIA_PIA_SPF(&c_strm->port.info) / + PJMEDIA_PIA_CCNT(&c_strm->port.info); else ts_len = 0; @@ -1422,13 +924,13 @@ static pj_status_t put_frame_imp( pjmedia_port *port, NULL, NULL); /* Update RTCP stats with last RTP timestamp. */ - stream->rtcp.stat.rtp_tx_last_ts = pj_ntohl(channel->rtp.out_hdr.ts); + c_strm->rtcp.stat.rtp_tx_last_ts = pj_ntohl(channel->rtp.out_hdr.ts); /* Check if now is the time to transmit RTCP SR/RR report. * We only do this when the decoder is paused, * because otherwise check_tx_rtcp() will be handled by on_rx_rtp(). */ - if (stream->dec->paused) { + if (c_strm->dec->paused) { check_tx_rtcp(stream, pj_ntohl(channel->rtp.out_hdr.ts)); } @@ -1439,7 +941,7 @@ static pj_status_t put_frame_imp( pjmedia_port *port, stream->tx_duration += ts_len; /* Init frame_out buffer. */ - frame_out.buf = ((char*)channel->out_pkt) + sizeof(pjmedia_rtp_hdr); + frame_out.buf = ((char*)channel->buf) + sizeof(pjmedia_rtp_hdr); frame_out.size = 0; /* If we have DTMF digits in the queue, transmit the digits. @@ -1485,8 +987,8 @@ static pj_status_t put_frame_imp( pjmedia_port *port, */ } else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO && frame->buf == NULL && - stream->port.info.fmt.id == PJMEDIA_FORMAT_L16 && - (stream->dir & PJMEDIA_DIR_ENCODING)) + c_strm->port.info.fmt.id == PJMEDIA_FORMAT_L16 && + (c_strm->dir & PJMEDIA_DIR_ENCODING)) { pjmedia_frame silence_frame; @@ -1494,15 +996,15 @@ static pj_status_t put_frame_imp( pjmedia_port *port, silence_frame.buf = stream->zero_frame; silence_frame.size = stream->enc_samples_per_pkt * 2; silence_frame.type = PJMEDIA_FRAME_TYPE_AUDIO; - silence_frame.timestamp.u32.lo = pj_ntohl(stream->enc->rtp.out_hdr.ts); + silence_frame.timestamp.u32.lo = pj_ntohl(c_strm->enc->rtp.out_hdr.ts); /* Encode! */ status = pjmedia_codec_encode( stream->codec, &silence_frame, - channel->out_pkt_size - + channel->buf_size - sizeof(pjmedia_rtp_hdr), &frame_out); if (status != PJ_SUCCESS) { - LOGERR_((stream->port.info.name.ptr, status, + LOGERR_((c_strm->port.info.name.ptr, status, "Codec encode() error")); return status; } @@ -1521,11 +1023,11 @@ static pj_status_t put_frame_imp( pjmedia_port *port, { /* Encode! */ status = pjmedia_codec_encode( stream->codec, frame, - channel->out_pkt_size - + channel->buf_size - sizeof(pjmedia_rtp_hdr), &frame_out); if (status != PJ_SUCCESS) { - LOGERR_((stream->port.info.name.ptr, status, + LOGERR_((c_strm->port.info.name.ptr, status, "Codec encode() error")); return status; } @@ -1549,7 +1051,7 @@ static pj_status_t put_frame_imp( pjmedia_port *port, } if (status != PJ_SUCCESS) { - LOGERR_((stream->port.info.name.ptr, status, + LOGERR_((c_strm->port.info.name.ptr, status, "RTP encode_rtp() error")); return status; } @@ -1558,14 +1060,14 @@ static pj_status_t put_frame_imp( pjmedia_port *port, * We only do this when stream direction is not "decoding only", because * when it is, check_tx_rtcp() will be handled by get_frame(). */ - if (stream->dir != PJMEDIA_DIR_DECODING) { + if (c_strm->dir != PJMEDIA_DIR_DECODING) { check_tx_rtcp(stream, pj_ntohl(channel->rtp.out_hdr.ts)); } /* Do nothing if we have nothing to transmit */ if (frame_out.size == 0) { if (stream->is_streaming) { - PJ_LOG(5,(stream->port.info.name.ptr,"Starting silence")); + PJ_LOG(5,(c_strm->port.info.name.ptr,"Starting silence")); stream->is_streaming = PJ_FALSE; } @@ -1574,7 +1076,7 @@ static pj_status_t put_frame_imp( pjmedia_port *port, /* Copy RTP header to the beginning of packet */ - pj_memcpy(channel->out_pkt, rtphdr, sizeof(pjmedia_rtp_hdr)); + pj_memcpy(channel->buf, rtphdr, sizeof(pjmedia_rtp_hdr)); /* Special case for DTMF: timestamp remains constant for * the same event, and is only updated after a complete event @@ -1587,37 +1089,37 @@ static pj_status_t put_frame_imp( pjmedia_port *port, /* Set RTP marker bit if currently not streaming */ if (stream->is_streaming == PJ_FALSE) { - pjmedia_rtp_hdr *rtp = (pjmedia_rtp_hdr*) channel->out_pkt; + pjmedia_rtp_hdr *rtp = (pjmedia_rtp_hdr*) channel->buf; rtp->m = 1; - PJ_LOG(5,(stream->port.info.name.ptr,"Starting talksprut..")); + PJ_LOG(5,(c_strm->port.info.name.ptr,"Starting talksprut..")); } stream->is_streaming = PJ_TRUE; /* Send the RTP packet to the transport. */ - status = pjmedia_transport_send_rtp(stream->transport, channel->out_pkt, + status = pjmedia_transport_send_rtp(c_strm->transport, channel->buf, frame_out.size + sizeof(pjmedia_rtp_hdr)); if (status != PJ_SUCCESS) { - if (stream->rtp_tx_err_cnt++ == 0) { - LOGERR_((stream->port.info.name.ptr, status, "Error sending RTP")); + if (c_strm->rtp_tx_err_cnt++ == 0) { + LOGERR_((c_strm->port.info.name.ptr, status, "Error sending RTP")); } - if (stream->rtp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) { - stream->rtp_tx_err_cnt = 0; + if (c_strm->rtp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) { + c_strm->rtp_tx_err_cnt = 0; } return PJ_SUCCESS; } /* Update stat */ - pjmedia_rtcp_tx_rtp(&stream->rtcp, (unsigned)frame_out.size); - stream->rtcp.stat.rtp_tx_last_ts = pj_ntohl(stream->enc->rtp.out_hdr.ts); - stream->rtcp.stat.rtp_tx_last_seq = pj_ntohs(stream->enc->rtp.out_hdr.seq); + pjmedia_rtcp_tx_rtp(&c_strm->rtcp, (unsigned)frame_out.size); + c_strm->rtcp.stat.rtp_tx_last_ts = pj_ntohl(c_strm->enc->rtp.out_hdr.ts); + c_strm->rtcp.stat.rtp_tx_last_seq = pj_ntohs(c_strm->enc->rtp.out_hdr.seq); #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /* Update time of last sending packet. */ - pj_gettimeofday(&stream->last_frm_ts_sent); + pj_gettimeofday(&c_strm->last_frm_ts_sent); #endif return PJ_SUCCESS; @@ -1635,6 +1137,7 @@ static pj_status_t put_frame( pjmedia_port *port, pjmedia_frame *frame ) { pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata; + pjmedia_stream_common *c_strm = &stream->base; pjmedia_frame tmp_zero_frame; unsigned samples_per_frame; @@ -1680,19 +1183,19 @@ static pj_status_t put_frame( pjmedia_port *port, */ if (stream->vad_enabled != stream->codec_param.setting.vad && (stream->tx_duration - stream->ts_vad_disabled) > - PJMEDIA_PIA_SRATE(&stream->port.info) * + PJMEDIA_PIA_SRATE(&c_strm->port.info) * PJMEDIA_STREAM_VAD_SUSPEND_MSEC / 1000) { stream->codec_param.setting.vad = stream->vad_enabled; pjmedia_codec_modify(stream->codec, &stream->codec_param); - PJ_LOG(4,(stream->port.info.name.ptr,"VAD re-enabled")); + PJ_LOG(4,(c_strm->port.info.name.ptr,"VAD re-enabled")); } /* If encoder has different ptime than decoder, then the frame must * be passed through the encoding buffer via rebuffer() function. */ - if (stream->enc_buf != NULL) { + if (c_strm->enc_buf != NULL) { pjmedia_frame tmp_rebuffer_frame; pj_status_t status = PJ_SUCCESS; @@ -1767,6 +1270,7 @@ static void handle_incoming_dtmf( pjmedia_stream *stream, const pj_timestamp *timestamp, const void *payload, unsigned payloadlen) { + pjmedia_stream_common *c_strm = &stream->base; pjmedia_rtp_dtmf_event *event = (pjmedia_rtp_dtmf_event*) payload; pj_uint16_t event_duration; pjmedia_stream_dtmf_event dtmf_event; @@ -1789,7 +1293,7 @@ static void handle_incoming_dtmf( pjmedia_stream *stream, #else if (event->event > 15) { #endif - PJ_LOG(5,(stream->port.info.name.ptr, + PJ_LOG(5,(c_strm->port.info.name.ptr, "Ignored RTP pkt with bad DTMF event %d", event->event)); return; @@ -1843,7 +1347,7 @@ static void handle_incoming_dtmf( pjmedia_stream *stream, } /* New event! */ - PJ_LOG(5,(stream->port.info.name.ptr, "Received DTMF digit %c, vol=%d", + PJ_LOG(5,(c_strm->port.info.name.ptr, "Received DTMF digit %c, vol=%d", digitmap[event->event], (event->e_vol & PJMEDIA_RTP_DTMF_EVENT_VOLUME_MASK))); @@ -1873,7 +1377,7 @@ static void handle_incoming_dtmf( pjmedia_stream *stream, /* By convention, we use jitter buffer's mutex to access shared * DTMF variables. */ - pj_mutex_lock(stream->jb_mutex); + pj_mutex_lock(c_strm->jb_mutex); if (stream->rx_dtmf_count >= PJ_ARRAY_SIZE(stream->rx_dtmf_buf)) { /* DTMF digits overflow. Discard the oldest digit. */ pj_array_erase(stream->rx_dtmf_buf, @@ -1882,188 +1386,25 @@ static void handle_incoming_dtmf( pjmedia_stream *stream, --stream->rx_dtmf_count; } stream->rx_dtmf_buf[stream->rx_dtmf_count++] = digitmap[event->event]; - pj_mutex_unlock(stream->jb_mutex); + pj_mutex_unlock(c_strm->jb_mutex); } } /* - * This callback is called by stream transport on receipt of packets - * in the RTP socket. + * This callback is called by common stream processing on receipt of + * packets in the RTP socket (i.e. called by on_rx_rtp() in + * stream_imp_common.c) */ -static void on_rx_rtp( pjmedia_tp_cb_param *param) +static pj_status_t on_stream_rx_rtp(pjmedia_stream_common *c_strm, + const pjmedia_rtp_hdr *hdr, + const void *payload, + unsigned payloadlen, + pjmedia_rtp_status seq_st, + pj_bool_t *pkt_discarded) { - pjmedia_stream *stream = (pjmedia_stream*) param->user_data; - void *pkt = param->pkt; - pj_ssize_t bytes_read = param->size; - pjmedia_channel *channel = stream->dec; - const pjmedia_rtp_hdr *hdr; - const void *payload; - unsigned payloadlen; - pjmedia_rtp_status seq_st; - pj_bool_t check_pt; - pj_status_t status; - pj_bool_t pkt_discarded = PJ_FALSE; - - /* Check for errors */ - if (bytes_read < 0) { - status = (pj_status_t)-bytes_read; - if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { - return; - } - - LOGERR_((stream->port.info.name.ptr, status, - "Unable to receive RTP packet")); - - if (status == PJ_ESOCKETSTOP) { - /* Publish receive error event. */ - publish_tp_event(PJMEDIA_EVENT_MEDIA_TP_ERR, status, PJ_TRUE, - PJMEDIA_DIR_DECODING, stream); - } - return; - } - - /* Ignore non-RTP keep-alive packets */ - if (bytes_read < (pj_ssize_t) sizeof(pjmedia_rtp_hdr)) - return; - - /* Update RTP and RTCP session. */ - status = pjmedia_rtp_decode_rtp(&channel->rtp, pkt, (int)bytes_read, - &hdr, &payload, &payloadlen); - if (status != PJ_SUCCESS) { - LOGERR_((stream->port.info.name.ptr, status, "RTP decode error")); - stream->rtcp.stat.rx.discard++; - return; - } - - /* Check if multiplexing is allowed and the payload indicates RTCP. */ - if (stream->si.rtcp_mux && hdr->pt >= 64 && hdr->pt <= 95) { - on_rx_rtcp(stream, pkt, bytes_read); - return; - } - - /* See if source address of RTP packet is different than the - * configured address, and check if we need to tell the - * media transport to switch RTP remote address. - */ - if (param->src_addr) { - pj_uint32_t peer_ssrc = channel->rtp.peer_ssrc; - pj_bool_t badssrc = PJ_FALSE; - - /* Check SSRC. */ - if (!channel->rtp.has_peer_ssrc && peer_ssrc == 0) - peer_ssrc = pj_ntohl(hdr->ssrc); - - if ((stream->si.has_rem_ssrc) && (pj_ntohl(hdr->ssrc) != peer_ssrc)) { - badssrc = PJ_TRUE; - } - - if (pj_sockaddr_cmp(&stream->rem_rtp_addr, param->src_addr) == 0) { - /* We're still receiving from rem_rtp_addr. */ - stream->rtp_src_cnt = 0; - stream->rem_rtp_flag = badssrc? 2: 1; - } else { - stream->rtp_src_cnt++; - - if (stream->rtp_src_cnt < PJMEDIA_RTP_NAT_PROBATION_CNT) { - if (stream->rem_rtp_flag == 1 || - (stream->rem_rtp_flag == 2 && badssrc)) - { - /* Only discard if: - * - we have ever received packet with good ssrc from - * remote address (rem_rtp_addr), or - * - we have ever received packet with bad ssrc from - * remote address and this packet also has bad ssrc. - */ - return; - } - if (!badssrc && stream->rem_rtp_flag != 1) - { - /* Immediately switch if we receive packet with the - * correct ssrc AND we never receive packets with - * good ssrc from rem_rtp_addr. - */ - param->rem_switch = PJ_TRUE; - } - } else { - /* Switch. We no longer receive packets from rem_rtp_addr. */ - param->rem_switch = PJ_TRUE; - } - - if (param->rem_switch) { - /* Set remote RTP address to source address */ - pj_sockaddr_cp(&stream->rem_rtp_addr, param->src_addr); - - /* Reset counter and flag */ - stream->rtp_src_cnt = 0; - stream->rem_rtp_flag = badssrc? 2: 1; - - /* Update RTCP peer ssrc */ - stream->rtcp.peer_ssrc = pj_ntohl(hdr->ssrc); - } - } - } - - /* Add ref counter to avoid premature destroy from callbacks */ - pj_grp_lock_add_ref(stream->grp_lock); - - pj_bzero(&seq_st, sizeof(seq_st)); - - /* Ignore the packet if decoder is paused */ - if (channel->paused) { - goto on_return; - } - - /* Update RTP session (also checks if RTP session can accept - * the incoming packet. - */ - check_pt = (hdr->pt != stream->rx_event_pt) && PJMEDIA_STREAM_CHECK_RTP_PT; - pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, check_pt); -#if !PJMEDIA_STREAM_CHECK_RTP_PT - if (!check_pt && hdr->pt != channel->rtp.out_pt && - hdr->pt != stream->rx_event_pt) - { - seq_st.status.flag.badpt = -1; - } -#endif - if (seq_st.status.value) { - TRC_ ((stream->port.info.name.ptr, - "RTP status: badpt=%d, badssrc=%d, dup=%d, " - "outorder=%d, probation=%d, restart=%d", - seq_st.status.flag.badpt, - seq_st.status.flag.badssrc, - seq_st.status.flag.dup, - seq_st.status.flag.outorder, - seq_st.status.flag.probation, - seq_st.status.flag.restart)); - - if (seq_st.status.flag.badpt) { - PJ_LOG(4,(stream->port.info.name.ptr, - "Bad RTP pt %d (expecting %d)", - hdr->pt, channel->rtp.out_pt)); - } - - if (!stream->si.has_rem_ssrc && seq_st.status.flag.badssrc) { - PJ_LOG(4,(stream->port.info.name.ptr, - "Changed RTP peer SSRC %d (previously %d)", - channel->rtp.peer_ssrc, stream->rtcp.peer_ssrc)); - stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc; - } - - - } - - /* Skip bad RTP packet */ - if (seq_st.status.flag.bad) { - pkt_discarded = PJ_TRUE; - goto on_return; - } - - /* Ignore if payloadlen is zero */ - if (payloadlen == 0) { - pkt_discarded = PJ_TRUE; - goto on_return; - } + pjmedia_stream *stream = (pjmedia_stream*) c_strm; + pj_status_t status = PJ_SUCCESS; /* Handle incoming DTMF. */ if (hdr->pt == stream->rx_event_pt) { @@ -2086,10 +1427,10 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) /* Put "good" packet to jitter buffer, or reset the jitter buffer * when RTP session is restarted. */ - pj_mutex_lock( stream->jb_mutex ); + pj_mutex_lock( c_strm->jb_mutex ); if (seq_st.status.flag.restart) { - status = pjmedia_jbuf_reset(stream->jb); - PJ_LOG(4,(stream->port.info.name.ptr, "Jitter buffer reset")); + status = pjmedia_jbuf_reset(c_strm->jb); + PJ_LOG(4,(c_strm->port.info.name.ptr, "Jitter buffer reset")); } else { /* * Packets may contain more than one frames, while the jitter @@ -2110,11 +1451,11 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) status = pjmedia_codec_parse(stream->codec, (void*)payload, payloadlen, &ts, &count, frames); if (status != PJ_SUCCESS) { - LOGERR_((stream->port.info.name.ptr, status, + LOGERR_((c_strm->port.info.name.ptr, status, "Codec parse() error")); count = 0; } else if (count == 0) { - PJ_LOG(2, (stream->port.info.name.ptr, "codec parsed 0 frames")); + PJ_LOG(2, (c_strm->port.info.name.ptr, "codec parsed 0 frames")); } else if (stream->detect_ptime_change && frames[0].bit_info > 0xFFFF) { @@ -2140,7 +1481,7 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) dec_ptime_denum; stream->dec_ptime = (pj_uint16_t)dec_ptime; stream->dec_ptime_denum = (pj_uint8_t)dec_ptime_denum; - pjmedia_jbuf_set_ptime2(stream->jb, stream->dec_ptime, + pjmedia_jbuf_set_ptime2(c_strm->jb, stream->dec_ptime, stream->dec_ptime_denum); pjmedia_rtcp_session_setting_default(&setting); @@ -2149,15 +1490,15 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) stream->dec_ptime * 1000 / stream->dec_ptime_denum, stream->codec_param.info.channel_cnt); - pjmedia_rtcp_update(&stream->rtcp, &setting); + pjmedia_rtcp_update(&c_strm->rtcp, &setting); - PJ_LOG(4, (stream->port.info.name.ptr, "codec decode " + PJ_LOG(4, (c_strm->port.info.name.ptr, "codec decode " "ptime change detected: %d/%d -> %d/%d", old_ptime, old_ptime_denum, dec_ptime, dec_ptime_denum)); /* Reset jitter buffer after ptime changed */ - pjmedia_jbuf_reset(stream->jb); + pjmedia_jbuf_reset(c_strm->jb); } #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0) @@ -2172,21 +1513,21 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) /* Make sure the detection performed only on two consecutive * packets with valid RTP sequence and no wrapped timestamp. */ - if (seq_st.diff == 1 && stream->rtp_rx_last_ts && - ts.u64 > stream->rtp_rx_last_ts && + if (seq_st.diff == 1 && c_strm->rtp_rx_last_ts && + ts.u64 > c_strm->rtp_rx_last_ts && stream->rtp_rx_last_cnt > 0) { unsigned peer_frm_ts_diff; unsigned frm_ts_span; /* Calculate actual frame timestamp span */ - frm_ts_span = PJMEDIA_PIA_SPF(&stream->port.info) / + frm_ts_span = PJMEDIA_PIA_SPF(&c_strm->port.info) / stream->codec_param.setting.frm_per_pkt/ - PJMEDIA_PIA_CCNT(&stream->port.info); + PJMEDIA_PIA_CCNT(&c_strm->port.info); /* Get remote frame timestamp span */ peer_frm_ts_diff = - ((pj_uint32_t)ts.u64-stream->rtp_rx_last_ts) / + ((pj_uint32_t)ts.u64-c_strm->rtp_rx_last_ts) / stream->rtp_rx_last_cnt; /* Possibilities remote's samples per frame for G.722 @@ -2211,12 +1552,12 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) stream->rtp_rx_ts_len_per_frame)); /* Reset jitter buffer once detection done */ - pjmedia_jbuf_reset(stream->jb); + pjmedia_jbuf_reset(c_strm->jb); } } } - stream->rtp_rx_last_ts = (pj_uint32_t)ts.u64; + c_strm->rtp_rx_last_ts = (pj_uint32_t)ts.u64; stream->rtp_rx_last_cnt = count; } @@ -2246,18 +1587,18 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) pj_bool_t discarded; ext_seq = (unsigned)(frames[i].timestamp.u64 / ts_span); - pjmedia_jbuf_put_frame2(stream->jb, frames[i].buf, frames[i].size, + pjmedia_jbuf_put_frame2(c_strm->jb, frames[i].buf, frames[i].size, frames[i].bit_info, ext_seq, &discarded); if (discarded) - pkt_discarded = PJ_TRUE; + *pkt_discarded = PJ_TRUE; } #if TRACE_JB - trace_jb_put(stream, hdr, payloadlen, count); + trace_jb_put(c_strm, hdr, payloadlen, count); #endif } - pj_mutex_unlock( stream->jb_mutex ); + pj_mutex_unlock( c_strm->jb_mutex ); /* Check if now is the time to transmit RTCP SR/RR report. @@ -2265,98 +1606,19 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) * if the encoder is paused, * because otherwise check_tx_rtcp() will be handled by put_frame() */ - if (stream->dir == PJMEDIA_DIR_DECODING || stream->enc->paused) { + if (c_strm->dir == PJMEDIA_DIR_DECODING || c_strm->enc->paused) { check_tx_rtcp(stream, pj_ntohl(hdr->ts)); } if (status != 0) { - LOGERR_((stream->port.info.name.ptr, status, + LOGERR_((c_strm->port.info.name.ptr, status, "Jitter buffer put() error")); - pkt_discarded = PJ_TRUE; + *pkt_discarded = PJ_TRUE; goto on_return; } on_return: - /* Update RTCP session */ - if (stream->rtcp.peer_ssrc == 0) - stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc; - - pjmedia_rtcp_rx_rtp2(&stream->rtcp, pj_ntohs(hdr->seq), - pj_ntohl(hdr->ts), payloadlen, pkt_discarded); - - /* RTCP-FB generic NACK */ - if (stream->rtcp.received >= 10 && seq_st.diff > 1 && - stream->send_rtcp_fb_nack && pj_ntohs(hdr->seq) >= seq_st.diff) - { - pj_uint16_t nlost, first_seq; - - /* Report only one NACK (last 17 losts) */ - nlost = PJ_MIN(seq_st.diff - 1, 17); - first_seq = pj_ntohs(hdr->seq) - nlost; - - pj_bzero(&stream->rtcp_fb_nack, sizeof(stream->rtcp_fb_nack)); - stream->rtcp_fb_nack.pid = first_seq; - while (--nlost) { - stream->rtcp_fb_nack.blp <<= 1; - stream->rtcp_fb_nack.blp |= 1; - } - - /* Send it immediately */ - status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, - PJ_FALSE, PJ_FALSE, PJ_TRUE); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->port.info.name.ptr, status, - "Error sending RTCP FB generic NACK")); - } else { - stream->initial_rr = PJ_TRUE; - } - } - - /* Send RTCP RR and SDES after we receive some RTP packets */ - if (stream->rtcp.received >= 10 && !stream->initial_rr) { - status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, - PJ_FALSE, PJ_FALSE, PJ_FALSE); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->port.info.name.ptr, status, - "Error sending initial RTCP RR")); - } else { - stream->initial_rr = PJ_TRUE; - } - } - - pj_grp_lock_dec_ref(stream->grp_lock); -} - - -/* - * This callback is called by stream transport on receipt of packets - * in the RTCP socket. - */ -static void on_rx_rtcp( void *data, - void *pkt, - pj_ssize_t bytes_read) -{ - pjmedia_stream *stream = (pjmedia_stream*) data; - pj_status_t status; - - /* Check for errors */ - if (bytes_read < 0) { - status = (pj_status_t)-bytes_read; - if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { - return; - } - LOGERR_((stream->port.info.name.ptr, status, - "Unable to receive RTCP packet")); - - if (status == PJ_ESOCKETSTOP) { - /* Publish receive error event. */ - publish_tp_event(PJMEDIA_EVENT_MEDIA_TP_ERR, status, PJ_FALSE, - PJMEDIA_DIR_DECODING, stream); - } - return; - } - - pjmedia_rtcp_rx_rtcp(&stream->rtcp, pkt, bytes_read); + return status; } @@ -2370,6 +1632,7 @@ static pj_status_t create_channel( pj_pool_t *pool, const pjmedia_stream_info *param, pjmedia_channel **p_channel) { + pjmedia_stream_common *c_strm = &stream->base; pjmedia_channel *channel; pj_status_t status; @@ -2380,7 +1643,7 @@ static pj_status_t create_channel( pj_pool_t *pool, /* Init channel info. */ - channel->stream = stream; + channel->stream = c_strm; channel->dir = dir; channel->paused = 1; channel->pt = pt; @@ -2392,31 +1655,31 @@ static pj_status_t create_channel( pj_pool_t *pool, unsigned max_rx_based_size; unsigned max_bps_based_size; - /* out_pkt buffer is used for sending and receiving, so lets calculate - * its size based on both. For receiving, we have stream->frame_size, + /* buf buffer is used for sending and receiving, so lets calculate + * its size based on both. For receiving, we have c_strm->frame_size, * which is used in configuring jitter buffer frame length. * For sending, it is based on codec max_bps info. */ - max_rx_based_size = stream->frame_size; + max_rx_based_size = c_strm->frame_size; max_bps_based_size = stream->codec_param.info.max_bps * PJMEDIA_MAX_FRAME_DURATION_MS / 8 / 1000; - channel->out_pkt_size = PJ_MAX(max_rx_based_size, max_bps_based_size); + channel->buf_size = PJ_MAX(max_rx_based_size, max_bps_based_size); /* Also include RTP header size (for sending) */ - channel->out_pkt_size += sizeof(pjmedia_rtp_hdr); + channel->buf_size += sizeof(pjmedia_rtp_hdr); - if (channel->out_pkt_size > PJMEDIA_MAX_MTU - + if (channel->buf_size > PJMEDIA_MAX_MTU - PJMEDIA_STREAM_RESV_PAYLOAD_LEN) { - channel->out_pkt_size = PJMEDIA_MAX_MTU - + channel->buf_size = PJMEDIA_MAX_MTU - PJMEDIA_STREAM_RESV_PAYLOAD_LEN; } } else { return PJ_ENOTSUP; } - channel->out_pkt = pj_pool_alloc(pool, channel->out_pkt_size); - PJ_ASSERT_RETURN(channel->out_pkt != NULL, PJ_ENOMEM); + channel->buf = pj_pool_alloc(pool, channel->buf_size); + PJ_ASSERT_RETURN(channel->buf != NULL, PJ_ENOMEM); @@ -2449,19 +1712,20 @@ static pj_status_t stream_event_cb(pjmedia_event *event, void *user_data) { pjmedia_stream *stream = (pjmedia_stream*)user_data; + pjmedia_stream_common *c_strm = &stream->base; /* Set RTCP FB capability in the event */ if (event->type==PJMEDIA_EVENT_RX_RTCP_FB && - event->epub==&stream->rtcp) + event->epub==&c_strm->rtcp) { pjmedia_event_rx_rtcp_fb_data *data = (pjmedia_event_rx_rtcp_fb_data*) &event->data.rx_rtcp_fb; /* Application not configured to listen to NACK, discard this event */ - if (stream->rtcp_fb_nack_cap_idx < 0) + if (c_strm->rtcp_fb_nack_cap_idx < 0) return PJ_SUCCESS; - data->cap = stream->si.loc_rtcp_fb.caps[stream->rtcp_fb_nack_cap_idx]; + data->cap = stream->si.loc_rtcp_fb.caps[c_strm->rtcp_fb_nack_cap_idx]; } /* Republish events */ @@ -2483,6 +1747,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, { enum { M = 32 }; pjmedia_stream *stream; + pjmedia_stream_common *c_strm; pj_str_t name; unsigned jb_init, jb_max, jb_min_pre, jb_max_pre; pjmedia_audio_format_detail *afd; @@ -2506,10 +1771,12 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, stream = PJ_POOL_ZALLOC_T(pool, pjmedia_stream); PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM); - stream->own_pool = own_pool; + c_strm = &stream->base; + c_strm->own_pool = own_pool; /* Duplicate stream info */ pj_memcpy(&stream->si, info, sizeof(*info)); + c_strm->si = (pjmedia_stream_info_common *)&stream->si; pj_strdup(pool, &stream->si.fmt.encoding_name, &info->fmt.encoding_name); if (info->param) stream->si.param = pjmedia_codec_param_clone(pool, info->param); @@ -2525,57 +1792,57 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, /* Init some port-info. Some parts of the info will be set later * once we have more info about the codec. */ - pjmedia_port_info_init(&stream->port.info, &name, + pjmedia_port_info_init(&c_strm->port.info, &name, PJMEDIA_SIG_PORT_STREAM, info->fmt.clock_rate, info->fmt.channel_cnt, 16, 80); - afd = pjmedia_format_get_audio_format_detail(&stream->port.info.fmt, 1); + afd = pjmedia_format_get_audio_format_detail(&c_strm->port.info.fmt, 1); //No longer there in 2.0 - //pj_strdup(pool, &stream->port.info.encoding_name, &info->fmt.encoding_name); + //pj_strdup(pool, &c_strm->port.info.encoding_name, &info->fmt.encoding_name); afd->clock_rate = info->fmt.clock_rate; afd->channel_count = info->fmt.channel_cnt; - stream->port.port_data.pdata = stream; + c_strm->port.port_data.pdata = stream; /* Init stream: */ - stream->endpt = endpt; + c_strm->endpt = endpt; stream->codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); - stream->dir = info->dir; - stream->user_data = user_data; - stream->rtcp_interval = (PJMEDIA_RTCP_INTERVAL-500 + (pj_rand()%1000)) * + c_strm->dir = info->dir; + c_strm->user_data = user_data; + c_strm->rtcp_interval = (PJMEDIA_RTCP_INTERVAL-500 + (pj_rand()%1000)) * info->fmt.clock_rate / 1000; - stream->rtcp_sdes_bye_disabled = info->rtcp_sdes_bye_disabled; + c_strm->rtcp_sdes_bye_disabled = info->rtcp_sdes_bye_disabled; stream->tx_event_pt = info->tx_event_pt ? info->tx_event_pt : -1; stream->rx_event_pt = info->rx_event_pt ? info->rx_event_pt : -1; stream->last_dtmf = -1; - stream->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME; - stream->rtcp_fb_nack.pid = -1; + c_strm->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME; + c_strm->rtcp_fb_nack.pid = -1; stream->soft_start_cnt = PJMEDIA_STREAM_SOFT_START; #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 - stream->use_ka = info->use_ka; - stream->ka_interval = info->ka_cfg.ka_interval; - stream->start_ka_count = info->ka_cfg.start_count; - stream->start_ka_interval = info->ka_cfg.start_interval; + c_strm->use_ka = info->use_ka; + c_strm->ka_interval = info->ka_cfg.ka_interval; + c_strm->start_ka_count = info->ka_cfg.start_count; + c_strm->start_ka_interval = info->ka_cfg.start_interval; #endif - stream->cname = info->cname; - if (stream->cname.slen == 0) { + c_strm->cname = info->cname; + if (c_strm->cname.slen == 0) { /* Build random RTCP CNAME. CNAME has user@host format */ - stream->cname.ptr = p = (char*) pj_pool_alloc(pool, 20); + c_strm->cname.ptr = p = (char*) pj_pool_alloc(pool, 20); pj_create_random_string(p, 5); p += 5; *p++ = '@'; *p++ = 'p'; *p++ = 'j'; pj_create_random_string(p, 6); p += 6; *p++ = '.'; *p++ = 'o'; *p++ = 'r'; *p++ = 'g'; - stream->cname.slen = p - stream->cname.ptr; + c_strm->cname.slen = p - c_strm->cname.ptr; } /* Create mutex to protect jitter buffer: */ - status = pj_mutex_create_simple(pool, NULL, &stream->jb_mutex); + status = pj_mutex_create_simple(pool, NULL, &c_strm->jb_mutex); if (status != PJ_SUCCESS) goto err_cleanup; @@ -2648,14 +1915,14 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, afd->frame_time_usec = stream->codec_param.info.frm_ptime * stream->codec_param.setting.frm_per_pkt * 1000 / stream->codec_param.info.frm_ptime_denum; - stream->port.info.fmt.id = stream->codec_param.info.fmt_id; + c_strm->port.info.fmt.id = stream->codec_param.info.fmt_id; if (stream->codec_param.info.fmt_id == PJMEDIA_FORMAT_L16) { /* Raw format */ afd->avg_bps = afd->max_bps = afd->clock_rate * afd->channel_count * afd->bits_per_sample; - stream->port.put_frame = &put_frame; - stream->port.get_frame = &get_frame; + c_strm->port.put_frame = &put_frame; + c_strm->port.get_frame = &get_frame; } else { /* Encoded format */ afd->avg_bps = stream->codec_param.info.avg_bps; @@ -2666,14 +1933,14 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, stream->codec_param.info.frm_ptime * stream->codec_param.setting.frm_per_pkt) % 8000 != 0) { - ++stream->port.info.bytes_per_frame; + ++c_strm->port.info.bytes_per_frame; } - stream->port.info.format.bitrate = stream->codec_param.info.avg_bps; - stream->port.info.format.vad = (stream->codec_param.setting.vad != 0); + c_strm->port.info.format.bitrate = stream->codec_param.info.avg_bps; + c_strm->port.info.format.vad = (stream->codec_param.setting.vad != 0); */ - stream->port.put_frame = &put_frame; - stream->port.get_frame = &get_frame_ext; + c_strm->port.put_frame = &put_frame; + c_strm->port.get_frame = &get_frame_ext; } /* If encoder and decoder's ptime are asymmetric, then we need to @@ -2718,7 +1985,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, /* Allocate buffer */ stream->enc_buf_size = afd->clock_rate * ptime / 1000 / 1000; - stream->enc_buf = (pj_int16_t*) + c_strm->enc_buf = (pj_int16_t*) pj_pool_alloc(pool, stream->enc_buf_size * 2); } else { @@ -2732,14 +1999,14 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, stream->codec_param.setting.vad = 0; stream->ts_vad_disabled = 0; pjmedia_codec_modify(stream->codec, &stream->codec_param); - PJ_LOG(4,(stream->port.info.name.ptr,"VAD temporarily disabled")); + PJ_LOG(4,(c_strm->port.info.name.ptr,"VAD temporarily disabled")); } /* Get the frame size */ if (stream->codec_param.info.max_rx_frame_size > 0) { - stream->frame_size = stream->codec_param.info.max_rx_frame_size; + c_strm->frame_size = stream->codec_param.info.max_rx_frame_size; } else { - stream->frame_size = stream->codec_param.info.max_bps * + c_strm->frame_size = stream->codec_param.info.max_bps * stream->codec_param.info.frm_ptime / stream->codec_param.info.frm_ptime_denum / 8 / 1000; @@ -2747,7 +2014,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, stream->codec_param.info.frm_ptime / stream->codec_param.info.frm_ptime_denum) % 8000 != 0) { - ++stream->frame_size; + ++c_strm->frame_size; } } @@ -2769,7 +2036,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0) stream->rtp_rx_check_cnt = 50; stream->has_g722_mpeg_bug = PJ_FALSE; - stream->rtp_rx_last_ts = 0; + c_strm->rtp_rx_last_ts = 0; stream->rtp_rx_last_cnt = 0; stream->rtp_tx_ts_len_per_pkt = stream->enc_samples_per_pkt / stream->codec_param.info.channel_cnt; @@ -2844,24 +2111,24 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, } /* Create jitter buffer */ - status = pjmedia_jbuf_create(pool, &stream->port.info.name, - stream->frame_size, + status = pjmedia_jbuf_create(pool, &c_strm->port.info.name, + c_strm->frame_size, stream->codec_param.info.frm_ptime, - jb_max, &stream->jb); + jb_max, &c_strm->jb); if (status != PJ_SUCCESS) goto err_cleanup; /* Set up jitter buffer */ - pjmedia_jbuf_set_ptime2(stream->jb, stream->codec_param.info.frm_ptime, + pjmedia_jbuf_set_ptime2(c_strm->jb, stream->codec_param.info.frm_ptime, stream->codec_param.info.frm_ptime_denum); - pjmedia_jbuf_set_adaptive( stream->jb, jb_init, jb_min_pre, jb_max_pre); - pjmedia_jbuf_set_discard(stream->jb, info->jb_discard_algo); + pjmedia_jbuf_set_adaptive( c_strm->jb, jb_init, jb_min_pre, jb_max_pre); + pjmedia_jbuf_set_discard(c_strm->jb, info->jb_discard_algo); /* Create decoder channel: */ status = create_channel( pool, stream, PJMEDIA_DIR_DECODING, - info->rx_pt, info, &stream->dec); + info->rx_pt, info, &c_strm->dec); if (status != PJ_SUCCESS) goto err_cleanup; @@ -2869,7 +2136,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, /* Create encoder channel: */ status = create_channel( pool, stream, PJMEDIA_DIR_ENCODING, - info->tx_pt, info, &stream->enc); + info->tx_pt, info, &c_strm->enc); if (status != PJ_SUCCESS) goto err_cleanup; @@ -2880,9 +2147,9 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, pjmedia_rtcp_session_setting rtcp_setting; pjmedia_rtcp_session_setting_default(&rtcp_setting); - rtcp_setting.name = stream->port.info.name.ptr; + rtcp_setting.name = c_strm->port.info.name.ptr; rtcp_setting.ssrc = info->ssrc; - rtcp_setting.rtp_ts_base = pj_ntohl(stream->enc->rtp.out_hdr.ts); + rtcp_setting.rtp_ts_base = pj_ntohl(c_strm->enc->rtp.out_hdr.ts); rtcp_setting.clock_rate = info->fmt.clock_rate; rtcp_setting.samples_per_frame = PJMEDIA_AFD_SPF(afd); @@ -2895,41 +2162,41 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, } #endif - pjmedia_rtcp_init2(&stream->rtcp, &rtcp_setting); + pjmedia_rtcp_init2(&c_strm->rtcp, &rtcp_setting); if (info->rtp_seq_ts_set) { - stream->rtcp.stat.rtp_tx_last_seq = info->rtp_seq; - stream->rtcp.stat.rtp_tx_last_ts = info->rtp_ts; + c_strm->rtcp.stat.rtp_tx_last_seq = info->rtp_seq; + c_strm->rtcp.stat.rtp_tx_last_ts = info->rtp_ts; } /* Subscribe to RTCP events */ pjmedia_event_subscribe(NULL, &stream_event_cb, stream, - &stream->rtcp); + &c_strm->rtcp); } /* Allocate outgoing RTCP buffer, should be enough to hold SR/RR, SDES, * BYE, and XR. */ - stream->out_rtcp_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) + + c_strm->out_rtcp_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) + sizeof(pjmedia_rtcp_common) + - (4 + (unsigned)stream->cname.slen) + + (4 + (unsigned)c_strm->cname.slen) + 32; #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) if (info->rtcp_xr_enabled) { - stream->out_rtcp_pkt_size += sizeof(pjmedia_rtcp_xr_pkt); + c_strm->out_rtcp_pkt_size += sizeof(pjmedia_rtcp_xr_pkt); } #endif - if (stream->out_rtcp_pkt_size > PJMEDIA_MAX_MTU) - stream->out_rtcp_pkt_size = PJMEDIA_MAX_MTU; + if (c_strm->out_rtcp_pkt_size > PJMEDIA_MAX_MTU) + c_strm->out_rtcp_pkt_size = PJMEDIA_MAX_MTU; - stream->out_rtcp_pkt = pj_pool_alloc(pool, stream->out_rtcp_pkt_size); + c_strm->out_rtcp_pkt = pj_pool_alloc(pool, c_strm->out_rtcp_pkt_size); pj_bzero(&att_param, sizeof(att_param)); att_param.stream = stream; att_param.media_type = PJMEDIA_TYPE_AUDIO; att_param.user_data = stream; pj_sockaddr_cp(&att_param.rem_addr, &info->rem_addr); - pj_sockaddr_cp(&stream->rem_rtp_addr, &info->rem_addr); + pj_sockaddr_cp(&c_strm->rem_rtp_addr, &info->rem_addr); if (stream->si.rtcp_mux) { pj_sockaddr_cp(&att_param.rem_rtcp, &info->rem_addr); } else if (pj_sockaddr_has_addr(&info->rem_rtcp)) { @@ -2941,24 +2208,24 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, /* Create group lock & attach handler */ status = pj_grp_lock_create_w_handler(pool, NULL, stream, - &stream_on_destroy, - &stream->grp_lock); + &on_destroy, + &c_strm->grp_lock); if (status != PJ_SUCCESS) goto err_cleanup; /* Add ref */ - pj_grp_lock_add_ref(stream->grp_lock); - stream->port.grp_lock = stream->grp_lock; + pj_grp_lock_add_ref(c_strm->grp_lock); + c_strm->port.grp_lock = c_strm->grp_lock; /* Only attach transport when stream is ready. */ - stream->transport = tp; + c_strm->transport = tp; status = pjmedia_transport_attach2(tp, &att_param); if (status != PJ_SUCCESS) goto err_cleanup; /* Also add ref the transport group lock */ - if (stream->transport->grp_lock) - pj_grp_lock_add_ref(stream->transport->grp_lock); + if (c_strm->transport->grp_lock) + pj_grp_lock_add_ref(c_strm->transport->grp_lock); #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) @@ -2966,39 +2233,39 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, if (info->rtcp_xr_enabled) { int i; - pjmedia_rtcp_enable_xr(&stream->rtcp, PJ_TRUE); + pjmedia_rtcp_enable_xr(&c_strm->rtcp, PJ_TRUE); /* Set RTCP XR TX interval */ if (info->rtcp_xr_interval != 0) - stream->rtcp_xr_interval = info->rtcp_xr_interval; + c_strm->rtcp_xr_interval = info->rtcp_xr_interval; else - stream->rtcp_xr_interval = (PJMEDIA_RTCP_INTERVAL + + c_strm->rtcp_xr_interval = (PJMEDIA_RTCP_INTERVAL + (pj_rand() % 8000)) * info->fmt.clock_rate / 1000; /* Additional third-party RTCP XR destination */ if (info->rtcp_xr_dest.addr.sa_family != 0) { - stream->rtcp_xr_dest_len = pj_sockaddr_get_len(&info->rtcp_xr_dest); - pj_memcpy(&stream->rtcp_xr_dest, &info->rtcp_xr_dest, - stream->rtcp_xr_dest_len); + c_strm->rtcp_xr_dest_len = pj_sockaddr_get_len(&info->rtcp_xr_dest); + pj_memcpy(&c_strm->rtcp_xr_dest, &info->rtcp_xr_dest, + c_strm->rtcp_xr_dest_len); } /* jitter buffer adaptive info */ i = PJMEDIA_RTCP_XR_JB_ADAPTIVE; - pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, + pjmedia_rtcp_xr_update_info(&c_strm->rtcp.xr_session, PJMEDIA_RTCP_XR_INFO_CONF_JBA, i); /* Jitter buffer aggressiveness info (estimated) */ i = 7; - pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, + pjmedia_rtcp_xr_update_info(&c_strm->rtcp.xr_session, PJMEDIA_RTCP_XR_INFO_CONF_JBR, i); /* Jitter buffer absolute maximum delay */ i = jb_max * stream->codec_param.info.frm_ptime / stream->codec_param.info.frm_ptime_denum; - pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, + pjmedia_rtcp_xr_update_info(&c_strm->rtcp.xr_session, PJMEDIA_RTCP_XR_INFO_JB_ABS_MAX, i); @@ -3011,7 +2278,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, #else i = PJMEDIA_RTCP_XR_PLC_DIS; #endif - pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, + pjmedia_rtcp_xr_update_info(&c_strm->rtcp.xr_session, PJMEDIA_RTCP_XR_INFO_CONF_PLC, i); } @@ -3026,8 +2293,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, if (rfi->caps[i].type == PJMEDIA_RTCP_FB_NACK && rfi->caps[i].param.slen == 0) { - stream->send_rtcp_fb_nack = PJ_TRUE; - PJ_LOG(4,(stream->port.info.name.ptr, + c_strm->send_rtcp_fb_nack = PJ_TRUE; + PJ_LOG(4,(c_strm->port.info.name.ptr, "Send RTCP-FB generic NACK")); break; } @@ -3035,7 +2302,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, } /* Check if we should process incoming RTCP-FB */ - stream->rtcp_fb_nack_cap_idx = -1; + c_strm->rtcp_fb_nack_cap_idx = -1; if (stream->si.loc_rtcp_fb.cap_count) { pjmedia_rtcp_fb_info *lfi = &stream->si.loc_rtcp_fb; unsigned i; @@ -3044,8 +2311,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, if (lfi->caps[i].type == PJMEDIA_RTCP_FB_NACK && lfi->caps[i].param.slen == 0) { - stream->rtcp_fb_nack_cap_idx = i; - PJ_LOG(4,(stream->port.info.name.ptr, + c_strm->rtcp_fb_nack_cap_idx = i; + PJ_LOG(4,(c_strm->port.info.name.ptr, "Receive RTCP-FB generic NACK")); break; } @@ -3064,14 +2331,14 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, } /* Send RTCP SDES */ - if (!stream->rtcp_sdes_bye_disabled) { + if (!c_strm->rtcp_sdes_bye_disabled) { pjmedia_stream_send_rtcp_sdes(stream); } #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /* NAT hole punching by sending KA packet via RTP transport. */ - if (stream->use_ka) - send_keep_alive_packet(stream); + if (c_strm->use_ka) + send_keep_alive_packet(c_strm); #endif #if TRACE_JB @@ -3081,25 +2348,25 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, pj_ansi_snprintf(trace_name, sizeof(trace_name), TRACE_JB_PATH_PREFIX "%s.csv", - stream->port.info.name.ptr); + c_strm->port.info.name.ptr); status = pj_file_open(pool, trace_name, PJ_O_WRONLY, - &stream->trace_jb_fd); + &c_strm->trace_jb_fd); if (status != PJ_SUCCESS) { - stream->trace_jb_fd = TRACE_JB_INVALID_FD; + c_strm->trace_jb_fd = TRACE_JB_INVALID_FD; PJ_PERROR(3,(THIS_FILE, status, "Failed creating RTP trace file '%s'", trace_name)); } else { - stream->trace_jb_buf = (char*)pj_pool_alloc(pool, PJ_LOG_MAX_SIZE); + c_strm->trace_jb_buf = (char*)pj_pool_alloc(pool, PJ_LOG_MAX_SIZE); /* Print column header */ - len = pj_ansi_snprintf(stream->trace_jb_buf, PJ_LOG_MAX_SIZE, + len = pj_ansi_snprintf(c_strm->trace_jb_buf, PJ_LOG_MAX_SIZE, "Time, Operation, Size, Frame Count, " "Frame type, RTP Seq, RTP TS, RTP M, " "JB size, JB burst level, JB prefetch\n"); if (len < 1 || len >= PJ_LOG_MAX_SIZE) len = PJ_LOG_MAX_SIZE-1; - pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); - pj_file_flush(stream->trace_jb_fd); + pj_file_write(c_strm->trace_jb_fd, c_strm->trace_jb_buf, &len); + pj_file_flush(c_strm->trace_jb_fd); } } #endif @@ -3107,7 +2374,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, /* Success! */ *p_stream = stream; - PJ_LOG(5,(THIS_FILE, "Stream %s created", stream->port.info.name.ptr)); + PJ_LOG(5,(THIS_FILE, "Stream %s created", c_strm->port.info.name.ptr)); return PJ_SUCCESS; @@ -3118,42 +2385,16 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, } -static void stream_on_destroy(void *arg) +static void on_stream_destroy(void *arg) { pjmedia_stream* stream = (pjmedia_stream*)arg; - /* This function may be called when stream is partly initialized. */ - - /* Release ref to transport */ - if (stream->transport && stream->transport->grp_lock) - pj_grp_lock_dec_ref(stream->transport->grp_lock); - /* Free codec. */ if (stream->codec) { pjmedia_codec_close(stream->codec); pjmedia_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec); stream->codec = NULL; } - - /* Free mutex */ - if (stream->jb_mutex) { - pj_mutex_destroy(stream->jb_mutex); - stream->jb_mutex = NULL; - } - - /* Destroy jitter buffer */ - if (stream->jb) - pjmedia_jbuf_destroy(stream->jb); - -#if TRACE_JB - if (TRACE_JB_OPENED(stream)) { - pj_file_close(stream->trace_jb_fd); - stream->trace_jb_fd = TRACE_JB_INVALID_FD; - } -#endif - - PJ_LOG(4,(stream->port.info.name.ptr, "Stream destroyed")); - pj_pool_safe_release(&stream->own_pool); } @@ -3162,24 +2403,27 @@ static void stream_on_destroy(void *arg) */ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); pj_status_t status; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); - PJ_LOG(4,(stream->port.info.name.ptr, "Stream destroying")); + PJ_LOG(4,(c_strm->port.info.name.ptr, "Stream destroying")); /* Stop the streaming */ - if (stream->enc) - stream->port.put_frame = NULL; - if (stream->dec) - stream->port.get_frame = NULL; + if (c_strm->enc) + c_strm->port.put_frame = NULL; + if (c_strm->dec) + c_strm->port.get_frame = NULL; /* Send RTCP BYE (also SDES & XR) */ - if (stream->transport && !stream->rtcp_sdes_bye_disabled) { + if (c_strm->transport && !c_strm->rtcp_sdes_bye_disabled) { #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) - send_rtcp(stream, PJ_TRUE, PJ_TRUE, stream->rtcp.xr_enabled, PJ_FALSE); + send_rtcp(c_strm, PJ_TRUE, PJ_TRUE, c_strm->rtcp.xr_enabled, + PJ_FALSE, PJ_FALSE, PJ_FALSE); #else - send_rtcp(stream, PJ_TRUE, PJ_TRUE, PJ_FALSE, PJ_FALSE); + send_rtcp(c_strm, PJ_TRUE, PJ_TRUE, PJ_FALSE, PJ_FALSE, + PJ_FALSE, PJ_FALSE); #endif } @@ -3187,16 +2431,16 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) * RFC 2833 RTP packet with 'End' flag set. */ if (stream->tx_dtmf_count && stream->tx_dtmf_buf[0].duration != 0 && - stream->transport && stream->jb_mutex) + c_strm->transport && c_strm->jb_mutex) { pjmedia_frame frame_out; - pjmedia_channel *channel = stream->enc; + pjmedia_channel *channel = c_strm->enc; int first=0, last=0; void *rtphdr; int rtphdrlen; pj_bzero(&frame_out, sizeof(frame_out)); - frame_out.buf = ((char*)channel->out_pkt) + sizeof(pjmedia_rtp_hdr); + frame_out.buf = ((char*)channel->buf) + sizeof(pjmedia_rtp_hdr); frame_out.size = 0; create_dtmf_payload(stream, &frame_out, 1, &first, &last); @@ -3212,38 +2456,38 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) &rtphdrlen); if (status == PJ_SUCCESS) { /* Copy RTP header to the beginning of packet */ - pj_memcpy(channel->out_pkt, rtphdr, sizeof(pjmedia_rtp_hdr)); + pj_memcpy(channel->buf, rtphdr, sizeof(pjmedia_rtp_hdr)); /* Send the RTP packet to the transport. */ - status = pjmedia_transport_send_rtp(stream->transport, - channel->out_pkt, + status = pjmedia_transport_send_rtp(c_strm->transport, + channel->buf, frame_out.size + sizeof(pjmedia_rtp_hdr)); } if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->port.info.name.ptr, status, + PJ_PERROR(4,(c_strm->port.info.name.ptr, status, "Error sending RTP/DTMF end packet")); } } /* Unsubscribe from RTCP session events */ pjmedia_event_unsubscribe(NULL, &stream_event_cb, stream, - &stream->rtcp); + &c_strm->rtcp); /* Detach from transport * MUST NOT hold stream mutex while detaching from transport, as * it may cause deadlock. See ticket #460 for the details. */ - if (stream->transport) { - pjmedia_transport_detach(stream->transport, stream); - //stream->transport = NULL; + if (c_strm->transport) { + pjmedia_transport_detach(c_strm->transport, stream); + //c_strm->transport = NULL; } - if (stream->grp_lock) { - pj_grp_lock_dec_ref(stream->grp_lock); + if (c_strm->grp_lock) { + pj_grp_lock_dec_ref(c_strm->grp_lock); } else { - stream_on_destroy(stream); + on_destroy(stream); } return PJ_SUCCESS; @@ -3255,7 +2499,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) */ PJ_DEF(char) pjmedia_stream_get_last_jb_frame_type(pjmedia_stream *stream) { - return stream->jb_last_frm; + pjmedia_stream_common *c_strm = &stream->base; + return c_strm->jb_last_frm; } @@ -3265,7 +2510,8 @@ PJ_DEF(char) pjmedia_stream_get_last_jb_frame_type(pjmedia_stream *stream) PJ_DEF(pj_status_t) pjmedia_stream_get_port( pjmedia_stream *stream, pjmedia_port **p_port ) { - *p_port = &stream->port; + pjmedia_stream_common *c_strm = &stream->base; + *p_port = &c_strm->port; return PJ_SUCCESS; } @@ -3275,7 +2521,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_port( pjmedia_stream *stream, */ PJ_DEF(pjmedia_transport*) pjmedia_stream_get_transport(pjmedia_stream *st) { - return st->transport; + return st->base.transport; } @@ -3284,23 +2530,24 @@ PJ_DEF(pjmedia_transport*) pjmedia_stream_get_transport(pjmedia_stream *st) */ PJ_DEF(pj_status_t) pjmedia_stream_start(pjmedia_stream *stream) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); - PJ_ASSERT_RETURN(stream && stream->enc && stream->dec, PJ_EINVALIDOP); + PJ_ASSERT_RETURN(stream && c_strm->enc && c_strm->dec, PJ_EINVALIDOP); - if (stream->enc && (stream->dir & PJMEDIA_DIR_ENCODING)) { - stream->enc->paused = 0; - //pjmedia_snd_stream_start(stream->enc->snd_stream); - PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream started")); + if (c_strm->enc && (c_strm->dir & PJMEDIA_DIR_ENCODING)) { + c_strm->enc->paused = 0; + //pjmedia_snd_stream_start(c_strm->enc->snd_stream); + PJ_LOG(4,(c_strm->port.info.name.ptr, "Encoder stream started")); } else { - PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream paused")); + PJ_LOG(4,(c_strm->port.info.name.ptr, "Encoder stream paused")); } - if (stream->dec && (stream->dir & PJMEDIA_DIR_DECODING)) { - stream->dec->paused = 0; - //pjmedia_snd_stream_start(stream->dec->snd_stream); - PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream started")); + if (c_strm->dec && (c_strm->dir & PJMEDIA_DIR_DECODING)) { + c_strm->dec->paused = 0; + //pjmedia_snd_stream_start(c_strm->dec->snd_stream); + PJ_LOG(4,(c_strm->port.info.name.ptr, "Decoder stream started")); } else { - PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream paused")); + PJ_LOG(4,(c_strm->port.info.name.ptr, "Decoder stream paused")); } return PJ_SUCCESS; @@ -3334,10 +2581,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_info( const pjmedia_stream *stream, PJ_DEF(pj_status_t) pjmedia_stream_get_stat( const pjmedia_stream *stream, pjmedia_rtcp_stat *stat) { - PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL); - - pj_memcpy(stat, &stream->rtcp.stat, sizeof(pjmedia_rtcp_stat)); - return PJ_SUCCESS; + return pjmedia_stream_common_get_stat((pjmedia_stream_common *)stream, + stat); } @@ -3346,11 +2591,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_stat( const pjmedia_stream *stream, */ PJ_DEF(pj_status_t) pjmedia_stream_reset_stat(pjmedia_stream *stream) { - PJ_ASSERT_RETURN(stream, PJ_EINVAL); - - pjmedia_rtcp_init_stat(&stream->rtcp.stat); - - return PJ_SUCCESS; + return pjmedia_stream_common_reset_stat((pjmedia_stream_common *)stream); } @@ -3361,10 +2602,12 @@ PJ_DEF(pj_status_t) pjmedia_stream_reset_stat(pjmedia_stream *stream) PJ_DEF(pj_status_t) pjmedia_stream_get_stat_xr( const pjmedia_stream *stream, pjmedia_rtcp_xr_stat *stat) { + const pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL); - if (stream->rtcp.xr_enabled) { - pj_memcpy(stat, &stream->rtcp.xr_session.stat, sizeof(pjmedia_rtcp_xr_stat)); + if (c_strm->rtcp.xr_enabled) { + pj_memcpy(stat, &c_strm->rtcp.xr_session.stat, sizeof(pjmedia_rtcp_xr_stat)); return PJ_SUCCESS; } return PJ_ENOTFOUND; @@ -3377,8 +2620,10 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_stat_xr( const pjmedia_stream *stream, PJ_DEF(pj_status_t) pjmedia_stream_get_stat_jbuf(const pjmedia_stream *stream, pjmedia_jb_state *state) { + const pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream && state, PJ_EINVAL); - return pjmedia_jbuf_get_state(stream->jb, state); + return pjmedia_jbuf_get_state(c_strm->jb, state); } /* @@ -3387,22 +2632,24 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_stat_jbuf(const pjmedia_stream *stream, PJ_DEF(pj_status_t) pjmedia_stream_pause( pjmedia_stream *stream, pjmedia_dir dir) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream, PJ_EINVAL); - if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) { - stream->enc->paused = 1; - PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream paused")); + if ((dir & PJMEDIA_DIR_ENCODING) && c_strm->enc) { + c_strm->enc->paused = 1; + PJ_LOG(4,(c_strm->port.info.name.ptr, "Encoder stream paused")); } - if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) { - stream->dec->paused = 1; + if ((dir & PJMEDIA_DIR_DECODING) && c_strm->dec) { + c_strm->dec->paused = 1; /* Also reset jitter buffer */ - pj_mutex_lock( stream->jb_mutex ); - pjmedia_jbuf_reset(stream->jb); - pj_mutex_unlock( stream->jb_mutex ); + pj_mutex_lock( c_strm->jb_mutex ); + pjmedia_jbuf_reset(c_strm->jb); + pj_mutex_unlock( c_strm->jb_mutex ); - PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream paused")); + PJ_LOG(4,(c_strm->port.info.name.ptr, "Decoder stream paused")); } return PJ_SUCCESS; @@ -3415,17 +2662,19 @@ PJ_DEF(pj_status_t) pjmedia_stream_pause( pjmedia_stream *stream, PJ_DEF(pj_status_t) pjmedia_stream_resume( pjmedia_stream *stream, pjmedia_dir dir) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream, PJ_EINVAL); - if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) { - stream->enc->paused = 0; - PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream resumed")); + if ((dir & PJMEDIA_DIR_ENCODING) && c_strm->enc) { + c_strm->enc->paused = 0; + PJ_LOG(4,(c_strm->port.info.name.ptr, "Encoder stream resumed")); } - if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) { - stream->dec->paused = 0; + if ((dir & PJMEDIA_DIR_DECODING) && c_strm->dec) { + c_strm->dec->paused = 0; stream->soft_start_cnt = PJMEDIA_STREAM_SOFT_START; - PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream resumed")); + PJ_LOG(4,(c_strm->port.info.name.ptr, "Decoder stream resumed")); } return PJ_SUCCESS; @@ -3444,6 +2693,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf2( pjmedia_stream *stream, const pj_str_t *digit_char, unsigned duration) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); pj_status_t status = PJ_SUCCESS; /* By convention we use jitter buffer mutex to access DTMF @@ -3456,7 +2706,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf2( pjmedia_stream *stream, return PJMEDIA_RTP_EREMNORFC2833; } - pj_mutex_lock(stream->jb_mutex); + pj_mutex_lock(c_strm->jb_mutex); if (stream->tx_dtmf_count+digit_char->slen >= (long)PJ_ARRAY_SIZE(stream->tx_dtmf_buf)) @@ -3514,7 +2764,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf2( pjmedia_stream *stream, } on_return: - pj_mutex_unlock(stream->jb_mutex); + pj_mutex_unlock(c_strm->jb_mutex); return status; } @@ -3536,12 +2786,14 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_dtmf( pjmedia_stream *stream, char *digits, unsigned *size) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream && digits && size, PJ_EINVAL); /* By convention, we use jitter buffer's mutex to access DTMF * digits resources. */ - pj_mutex_lock(stream->jb_mutex); + pj_mutex_lock(c_strm->jb_mutex); if (stream->rx_dtmf_count < *size) *size = stream->rx_dtmf_count; @@ -3556,7 +2808,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_dtmf( pjmedia_stream *stream, } } - pj_mutex_unlock(stream->jb_mutex); + pj_mutex_unlock(c_strm->jb_mutex); return PJ_SUCCESS; } @@ -3571,17 +2823,19 @@ PJ_DEF(pj_status_t) pjmedia_stream_set_dtmf_callback(pjmedia_stream *stream, int digit), void *user_data) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream, PJ_EINVAL); /* By convention, we use jitter buffer's mutex to access DTMF * digits resources. */ - pj_mutex_lock(stream->jb_mutex); + pj_mutex_lock(c_strm->jb_mutex); stream->dtmf_cb = cb; stream->dtmf_cb_user_data = user_data; - pj_mutex_unlock(stream->jb_mutex); + pj_mutex_unlock(c_strm->jb_mutex); return PJ_SUCCESS; } @@ -3592,17 +2846,19 @@ PJ_DEF(pj_status_t) pjmedia_stream_set_dtmf_event_callback(pjmedia_stream *strea const pjmedia_stream_dtmf_event *event), void *user_data) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream, PJ_EINVAL); /* By convention, we use jitter buffer's mutex to access DTMF * digits resources. */ - pj_mutex_lock(stream->jb_mutex); + pj_mutex_lock(c_strm->jb_mutex); stream->dtmf_event_cb = cb; stream->dtmf_event_cb_user_data = user_data; - pj_mutex_unlock(stream->jb_mutex); + pj_mutex_unlock(c_strm->jb_mutex); return PJ_SUCCESS; } @@ -3613,9 +2869,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_set_dtmf_event_callback(pjmedia_stream *strea PJ_DEF(pj_status_t) pjmedia_stream_send_rtcp_sdes( pjmedia_stream *stream ) { - PJ_ASSERT_RETURN(stream, PJ_EINVAL); - - return send_rtcp(stream, PJ_TRUE, PJ_FALSE, PJ_FALSE, PJ_FALSE); + return pjmedia_stream_common_send_rtcp_sdes( + (pjmedia_stream_common *) stream); } /* @@ -3624,13 +2879,8 @@ pjmedia_stream_send_rtcp_sdes( pjmedia_stream *stream ) PJ_DEF(pj_status_t) pjmedia_stream_send_rtcp_bye( pjmedia_stream *stream ) { - PJ_ASSERT_RETURN(stream, PJ_EINVAL); - - if (stream->enc && stream->transport) { - return send_rtcp(stream, PJ_TRUE, PJ_TRUE, PJ_FALSE, PJ_FALSE); - } - - return PJ_SUCCESS; + return pjmedia_stream_common_send_rtcp_bye( + (pjmedia_stream_common *) stream); } @@ -3641,8 +2891,6 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_rtp_session_info(pjmedia_stream *stream, pjmedia_stream_rtp_sess_info *session_info) { - session_info->rx_rtp = &stream->dec->rtp; - session_info->tx_rtp = &stream->enc->rtp; - session_info->rtcp = &stream->rtcp; - return PJ_SUCCESS; + return pjmedia_stream_common_get_rtp_session_info( + (pjmedia_stream_common *)stream, session_info); } diff --git a/pjmedia/src/pjmedia/stream_common.c b/pjmedia/src/pjmedia/stream_common.c index 95bb1a64fe..6127a02f6a 100644 --- a/pjmedia/src/pjmedia/stream_common.c +++ b/pjmedia/src/pjmedia/stream_common.c @@ -16,10 +16,282 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #include +#include #define THIS_FILE "stream_common.c" +#define LOGERR_(expr) PJ_PERROR(4,expr); + +/* Number of send error before repeat the report. */ +#define SEND_ERR_COUNT_TO_REPORT 50 + +static const pj_str_t ID_IN = { "IN", 2 }; +static const pj_str_t ID_IP4 = { "IP4", 3}; +static const pj_str_t ID_IP6 = { "IP6", 3}; + +/* + * Create stream info from SDP media line. + */ +PJ_DEF(pj_status_t) pjmedia_stream_info_common_from_sdp( + pjmedia_stream_info_common *si, + pj_pool_t *pool, + pjmedia_endpt *endpt, + const pjmedia_sdp_session *local, + const pjmedia_sdp_session *remote, + unsigned stream_idx, + pj_bool_t *active) +{ + const pj_str_t STR_INACTIVE = { "inactive", 8 }; + const pj_str_t STR_SENDONLY = { "sendonly", 8 }; + const pj_str_t STR_RECVONLY = { "recvonly", 8 }; + + const pjmedia_sdp_attr *attr; + const pjmedia_sdp_media *local_m; + const pjmedia_sdp_media *rem_m; + const pjmedia_sdp_conn *local_conn; + const pjmedia_sdp_conn *rem_conn; + int rem_af, local_af; + unsigned i; + pj_status_t status; + + /* Init */ + *active = PJ_FALSE; + + /* Validate arguments: */ + PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); + PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); + PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); + + /* Keep SDP shortcuts */ + local_m = local->media[stream_idx]; + rem_m = remote->media[stream_idx]; + + local_conn = local_m->conn ? local_m->conn : local->conn; + if (local_conn == NULL) + return PJMEDIA_SDP_EMISSINGCONN; + + rem_conn = rem_m->conn ? rem_m->conn : remote->conn; + if (rem_conn == NULL) + return PJMEDIA_SDP_EMISSINGCONN; + + /* Reset: */ + pj_bzero(si, sizeof(*si)); + +#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR + /* Set default RTCP XR enabled/disabled */ + si->rtcp_xr_enabled = PJ_TRUE; +#endif + + /* Media type: */ + si->type = pjmedia_get_type(&local_m->desc.media); + + /* Transport protocol */ + + /* At this point, transport type must be compatible, + * the transport instance will do more validation later. + */ + status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, + &local_m->desc.transport); + if (status != PJ_SUCCESS) + return PJMEDIA_SDPNEG_EINVANSTP; + + /* Get the transport protocol */ + si->proto = pjmedia_sdp_transport_get_proto(&local_m->desc.transport); + + /* Just return success if stream is not RTP/AVP compatible */ + if (!PJMEDIA_TP_PROTO_HAS_FLAG(si->proto, PJMEDIA_TP_PROTO_RTP_AVP)) + return PJ_SUCCESS; + + /* Check address family in remote SDP */ + rem_af = pj_AF_UNSPEC(); + if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) { + if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) { + rem_af = pj_AF_INET(); + } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) { + rem_af = pj_AF_INET6(); + } + } + + if (rem_af==pj_AF_UNSPEC()) { + /* Unsupported address family */ + return PJ_EAFNOTSUP; + } + + /* Set remote address: */ + status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, + rem_m->desc.port); + if (status == PJ_ERESOLVE && rem_af == pj_AF_INET()) { + /* Handle special case in NAT64 scenario where for some reason, server + * puts IPv6 (literal or FQDN) in SDP answer while indicating "IP4" + * in its address type, let's retry resolving using AF_INET6. + */ + status = pj_sockaddr_init(pj_AF_INET6(), &si->rem_addr, + &rem_conn->addr, rem_m->desc.port); + } + if (status != PJ_SUCCESS) { + /* Invalid IP address. */ + return PJMEDIA_EINVALIDIP; + } + + /* Check address family of local info */ + local_af = pj_AF_UNSPEC(); + if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) { + if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) { + local_af = pj_AF_INET(); + } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) { + local_af = pj_AF_INET6(); + } + } + + if (local_af==pj_AF_UNSPEC()) { + /* Unsupported address family */ + return PJ_SUCCESS; + } + + /* Set remote address: */ + status = pj_sockaddr_init(local_af, &si->local_addr, &local_conn->addr, + local_m->desc.port); + if (status != PJ_SUCCESS) { + /* Invalid IP address. */ + return PJMEDIA_EINVALIDIP; + } + + /* Local and remote address family must match, except when ICE is used + * by both sides (see also ticket #1952). + */ + if (local_af != rem_af) { + const pj_str_t STR_ICE_CAND = { "candidate", 9 }; + if (pjmedia_sdp_media_find_attr(rem_m, &STR_ICE_CAND, NULL)==NULL || + pjmedia_sdp_media_find_attr(local_m, &STR_ICE_CAND, NULL)==NULL) + { + return PJ_EAFNOTSUP; + } + } + + /* Media direction: */ + if (local_m->desc.port == 0 || + pj_sockaddr_has_addr(&si->local_addr)==PJ_FALSE || + pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE || + pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) + { + /* Inactive stream. */ + + si->dir = PJMEDIA_DIR_NONE; + + } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { + + /* Send only stream. */ + + si->dir = PJMEDIA_DIR_ENCODING; + + } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { + + /* Recv only stream. */ + + si->dir = PJMEDIA_DIR_DECODING; + + } else { + + /* Send and receive stream. */ + + si->dir = PJMEDIA_DIR_ENCODING_DECODING; + + } + + /* No need to do anything else if stream is rejected */ + if (local_m->desc.port == 0) { + return PJ_SUCCESS; + } + + /* Check if "rtcp-mux" is present in the SDP. */ + attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, + "rtcp-mux", NULL); + if (attr) + si->rtcp_mux = PJ_TRUE; + + /* If "rtcp" attribute is present in the SDP, set the RTCP address + * from that attribute. Otherwise, calculate from RTP address. + */ + attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, + "rtcp", NULL); + if (attr) { + pjmedia_sdp_rtcp_attr rtcp; + status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); + if (status == PJ_SUCCESS) { + if (rtcp.addr.slen) { + status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr, + (pj_uint16_t)rtcp.port); + if (status != PJ_SUCCESS) + return PJMEDIA_EINVALIDIP; + } else { + pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, + (pj_uint16_t)rtcp.port); + pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp), + pj_sockaddr_get_addr(&si->rem_addr), + pj_sockaddr_get_addr_len(&si->rem_addr)); + } + } + } + + if (!pj_sockaddr_has_addr(&si->rem_rtcp)) { + int rtcp_port; + + pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr)); + rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1; + pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port); + } + + /* Check if "ssrc" attribute is present in the SDP. */ + for (i = 0; i < rem_m->attr_count; i++) { + if (pj_strcmp2(&rem_m->attr[i]->name, "ssrc") == 0) { + pjmedia_sdp_ssrc_attr ssrc; + + status = pjmedia_sdp_attr_get_ssrc( + (const pjmedia_sdp_attr *)rem_m->attr[i], &ssrc); + if (status == PJ_SUCCESS) { + si->has_rem_ssrc = PJ_TRUE; + si->rem_ssrc = ssrc.ssrc; + if (ssrc.cname.slen > 0) { + pj_strdup(pool, &si->rem_cname, &ssrc.cname); + break; + } + } + } + } + + /* Leave SSRC to random. */ + si->ssrc = pj_rand(); + + /* Set default jitter buffer parameter. */ + si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; + si->jb_discard_algo = PJMEDIA_JB_DISCARD_PROGRESSIVE; + + if (pjmedia_get_type(&local_m->desc.media) == PJMEDIA_TYPE_AUDIO || + pjmedia_get_type(&local_m->desc.media) == PJMEDIA_TYPE_VIDEO) + { + /* Get local RTCP-FB info */ + if (pjmedia_get_type(&local_m->desc.media) == PJMEDIA_TYPE_AUDIO || + pjmedia_get_type(&local_m->desc.media) == PJMEDIA_TYPE_VIDEO) + status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, local, + stream_idx, si->rx_pt, + &si->loc_rtcp_fb); + if (status != PJ_SUCCESS) + return status; + + /* Get remote RTCP-FB info */ + status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, remote, + stream_idx, si->tx_pt, + &si->rem_rtcp_fb); + if (status != PJ_SUCCESS) + return status; + } + + *active = PJ_TRUE; + return status; +} + #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 PJ_DEF(void) @@ -149,3 +421,261 @@ PJ_DECL(pj_status_t) pjmedia_stream_info_parse_fmtp_data(pj_pool_t *pool, return PJ_SUCCESS; } +/* + * Get stream statistics. + */ +PJ_DEF(pj_status_t) +pjmedia_stream_common_get_stat( const pjmedia_stream_common *c_strm, + pjmedia_rtcp_stat *stat) +{ + PJ_ASSERT_RETURN(c_strm && stat, PJ_EINVAL); + + pj_memcpy(stat, &c_strm->rtcp.stat, sizeof(pjmedia_rtcp_stat)); + return PJ_SUCCESS; +} + +/* + * Reset the stream statistics in the middle of a stream session. + */ +PJ_DEF(pj_status_t) +pjmedia_stream_common_reset_stat(pjmedia_stream_common *c_strm) +{ + PJ_ASSERT_RETURN(c_strm, PJ_EINVAL); + + pjmedia_rtcp_init_stat(&c_strm->rtcp.stat); + + return PJ_SUCCESS; +} + +/* + * Send RTCP SDES. + */ +PJ_DEF(pj_status_t) +pjmedia_stream_common_send_rtcp_sdes( pjmedia_stream_common *stream ) +{ + PJ_ASSERT_RETURN(stream, PJ_EINVAL); + + return pjmedia_stream_send_rtcp(stream, PJ_TRUE, PJ_FALSE, PJ_FALSE, + PJ_FALSE, PJ_FALSE, PJ_FALSE); +} + +/* + * Send RTCP BYE. + */ +PJ_DEF(pj_status_t) +pjmedia_stream_common_send_rtcp_bye( pjmedia_stream_common *c_strm ) +{ + PJ_ASSERT_RETURN(c_strm, PJ_EINVAL); + + if (c_strm->enc && c_strm->transport) { + return pjmedia_stream_send_rtcp(c_strm, PJ_TRUE, PJ_TRUE, PJ_FALSE, + PJ_FALSE, PJ_FALSE, PJ_FALSE); + } + + return PJ_SUCCESS; +} + +/** + * Get RTP session information from stream. + */ +PJ_DEF(pj_status_t) +pjmedia_stream_common_get_rtp_session_info(pjmedia_stream_common *c_strm, + pjmedia_stream_rtp_sess_info *session_info) +{ + session_info->rx_rtp = &c_strm->dec->rtp; + session_info->tx_rtp = &c_strm->enc->rtp; + session_info->rtcp = &c_strm->rtcp; + return PJ_SUCCESS; +} + +static pj_status_t build_rtcp_fb(pjmedia_stream_common *c_strm, void *buf, + pj_size_t *length) +{ + pj_status_t status; + + /* Generic NACK */ + if (c_strm->send_rtcp_fb_nack && c_strm->rtcp_fb_nack.pid >= 0) + { + status = pjmedia_rtcp_fb_build_nack(&c_strm->rtcp, buf, length, 1, + &c_strm->rtcp_fb_nack); + if (status != PJ_SUCCESS) + return status; + + /* Reset Packet ID */ + c_strm->rtcp_fb_nack.pid = -1; + } + + return PJ_SUCCESS; +} + +pj_status_t pjmedia_stream_send_rtcp(pjmedia_stream_common *c_strm, + pj_bool_t with_sdes, + pj_bool_t with_bye, + pj_bool_t with_xr, + pj_bool_t with_fb, + pj_bool_t with_fb_nack, + pj_bool_t with_fb_pli) +{ + void *sr_rr_pkt; + pj_uint8_t *pkt; + int len, max_len; + pj_status_t status; + + /* We need to prevent data race since there is only a single instance + * of rtcp packet buffer. And to avoid deadlock with media transport, + * we use the transport's group lock. + */ + if (c_strm->transport->grp_lock) + pj_grp_lock_acquire(c_strm->transport->grp_lock); + + /* Build RTCP RR/SR packet */ + pjmedia_rtcp_build_rtcp(&c_strm->rtcp, &sr_rr_pkt, &len); + +#if !defined(PJMEDIA_HAS_RTCP_XR) || (PJMEDIA_HAS_RTCP_XR == 0) + with_xr = PJ_FALSE; +#endif + + if (with_sdes || with_bye || with_xr || with_fb || with_fb_nack || + with_fb_pli) + { + pkt = (pj_uint8_t*) c_strm->out_rtcp_pkt; + pj_memcpy(pkt, sr_rr_pkt, len); + max_len = c_strm->out_rtcp_pkt_size; + } else { + pkt = (pj_uint8_t*)sr_rr_pkt; + max_len = len; + } + + /* Build RTCP SDES packet, forced if also send RTCP-FB */ + with_sdes = with_sdes || with_fb_pli || with_fb_nack; + + /* Build RTCP SDES packet */ + if (with_sdes) { + pjmedia_rtcp_sdes sdes; + pj_size_t sdes_len; + + pj_bzero(&sdes, sizeof(sdes)); + sdes.cname = c_strm->cname; + sdes_len = max_len - len; + status = pjmedia_rtcp_build_rtcp_sdes(&c_strm->rtcp, pkt+len, + &sdes_len, &sdes); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(c_strm->port.info.name.ptr, status, + "Error generating RTCP SDES")); + } else { + len += (int)sdes_len; + } + } + + if (with_fb) { + pj_size_t fb_len = max_len - len; + status = build_rtcp_fb(c_strm, pkt+len, &fb_len); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(c_strm->port.info.name.ptr, status, + "Error generating RTCP FB")); + } else { + len += (int)fb_len; + } + } + + /* Build RTCP XR packet */ +#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) + if (with_xr) { + int i; + pjmedia_jb_state jb_state; + void *xr_pkt; + int xr_len; + + /* Update RTCP XR with current JB states */ + pjmedia_jbuf_get_state(c_strm->jb, &jb_state); + + i = jb_state.avg_delay; + status = pjmedia_rtcp_xr_update_info(&c_strm->rtcp.xr_session, + PJMEDIA_RTCP_XR_INFO_JB_NOM, i); + pj_assert(status == PJ_SUCCESS); + + i = jb_state.max_delay; + status = pjmedia_rtcp_xr_update_info(&c_strm->rtcp.xr_session, + PJMEDIA_RTCP_XR_INFO_JB_MAX, i); + pj_assert(status == PJ_SUCCESS); + + pjmedia_rtcp_build_rtcp_xr(&c_strm->rtcp.xr_session, 0, + &xr_pkt, &xr_len); + + if (xr_len + len <= max_len) { + pj_memcpy(pkt+len, xr_pkt, xr_len); + len += xr_len; + + /* Send the RTCP XR to third-party destination if specified */ + if (c_strm->rtcp_xr_dest_len) { + pjmedia_transport_send_rtcp2(c_strm->transport, + &c_strm->rtcp_xr_dest, + c_strm->rtcp_xr_dest_len, + xr_pkt, xr_len); + } + + } else { + PJ_PERROR(4,(c_strm->port.info.name.ptr, PJ_ETOOBIG, + "Error generating RTCP-XR")); + } + } +#endif + + /* Build RTCP BYE packet */ + if (with_bye) { + pj_size_t bye_len; + + bye_len = max_len - len; + status = pjmedia_rtcp_build_rtcp_bye(&c_strm->rtcp, pkt+len, + &bye_len, NULL); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(c_strm->port.info.name.ptr, status, + "Error generating RTCP BYE")); + } else { + len += (int)bye_len; + } + } + + /* Build RTCP-FB generic NACK packet */ + if (with_fb_nack && c_strm->rtcp_fb_nack.pid >= 0) { + pj_size_t fb_len = max_len - len; + status = pjmedia_rtcp_fb_build_nack(&c_strm->rtcp, pkt+len, &fb_len, + 1, &c_strm->rtcp_fb_nack); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(c_strm->port.info.name.ptr, status, + "Error generating RTCP-FB NACK")); + } else { + len += (int)fb_len; + } + } + + /* Build RTCP-FB PLI packet */ + if (with_fb_pli) { + pj_size_t fb_len = max_len - len; + status = pjmedia_rtcp_fb_build_pli(&c_strm->rtcp, pkt+len, &fb_len); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(c_strm->port.info.name.ptr, status, + "Error generating RTCP-FB PLI")); + } else { + len += (int)fb_len; + PJ_LOG(5,(c_strm->name.ptr, "Sending RTCP-FB PLI packet")); + } + } + + /* Send! */ + status = pjmedia_transport_send_rtcp(c_strm->transport, pkt, len); + if (status != PJ_SUCCESS) { + if (c_strm->rtcp_tx_err_cnt++ == 0) { + LOGERR_((c_strm->port.info.name.ptr, status, + "Error sending RTCP")); + } + if (c_strm->rtcp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) { + c_strm->rtcp_tx_err_cnt = 0; + } + } + + if (c_strm->transport->grp_lock) + pj_grp_lock_release(c_strm->transport->grp_lock); + + return status; +} diff --git a/pjmedia/src/pjmedia/stream_imp_common.c b/pjmedia/src/pjmedia/stream_imp_common.c new file mode 100755 index 0000000000..7b38541c2a --- /dev/null +++ b/pjmedia/src/pjmedia/stream_imp_common.c @@ -0,0 +1,599 @@ +/* + * Copyright (C) 2025 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Prototypes. */ +/* Specific stream implementation's RX RTP handler. */ +static pj_status_t on_stream_rx_rtp(pjmedia_stream_common *c_strm, + const pjmedia_rtp_hdr *hdr, + const void *payload, + unsigned payloadlen, + pjmedia_rtp_status seq_st, + pj_bool_t *pkt_discarded); + +/* Specific stream implementation's destroy handler. */ +static void on_stream_destroy(void *arg); + +#if TRACE_JB + +#include + +#define TRACE_JB_INVALID_FD ((pj_oshandle_t)-1) +#define TRACE_JB_OPENED(s) (s->trace_jb_fd != TRACE_JB_INVALID_FD) + +PJ_INLINE(int) trace_jb_print_timestamp(char **buf, pj_ssize_t len) +{ + pj_time_val now; + pj_parsed_time ptime; + char *p = *buf; + + if (len < 14) + return -1; + + pj_gettimeofday(&now); + pj_time_decode(&now, &ptime); + p += pj_utoa_pad(ptime.hour, p, 2, '0'); + *p++ = ':'; + p += pj_utoa_pad(ptime.min, p, 2, '0'); + *p++ = ':'; + p += pj_utoa_pad(ptime.sec, p, 2, '0'); + *p++ = '.'; + p += pj_utoa_pad(ptime.msec, p, 3, '0'); + *p++ = ','; + + *buf = p; + + return 0; +} + +PJ_INLINE(int) trace_jb_print_state(pjmedia_stream_common *c_strm, + char **buf, pj_ssize_t len) +{ + char *p = *buf; + char *endp = *buf + len; + pjmedia_jb_state state; + + pjmedia_jbuf_get_state(c_strm->jb, &state); + + len = pj_ansi_snprintf(p, endp-p, "%d, %d, %d", + state.size, state.burst, state.prefetch); + if ((len < 0) || (len >= endp-p)) + return -1; + + p += len; + *buf = p; + return 0; +} + +static void trace_jb_get(pjmedia_stream_common *c_strm, pjmedia_jb_frame_type ft, + pj_size_t fsize) +{ + char *p = c_strm->trace_jb_buf; + char *endp = c_strm->trace_jb_buf + PJ_LOG_MAX_SIZE; + pj_ssize_t len = 0; + const char* ft_st; + + if (!TRACE_JB_OPENED(c_strm)) + return; + + /* Print timestamp. */ + if (trace_jb_print_timestamp(&p, endp-p)) + goto on_insuff_buffer; + + /* Print frame type and size */ + switch(ft) { + case PJMEDIA_JB_MISSING_FRAME: + ft_st = "missing"; + break; + case PJMEDIA_JB_NORMAL_FRAME: + ft_st = "normal"; + break; + case PJMEDIA_JB_ZERO_PREFETCH_FRAME: + ft_st = "prefetch"; + break; + case PJMEDIA_JB_ZERO_EMPTY_FRAME: + ft_st = "empty"; + break; + default: + ft_st = "unknown"; + break; + } + + /* Print operation, size, frame count, frame type */ + len = pj_ansi_snprintf(p, endp-p, "GET,%zu,1,%s,,,,", fsize, ft_st); + if ((len < 0) || (len >= endp-p)) + goto on_insuff_buffer; + p += len; + + /* Print JB state */ + if (trace_jb_print_state(c_strm, &p, endp-p)) + goto on_insuff_buffer; + + /* Print end of line */ + if (endp-p < 2) + goto on_insuff_buffer; + *p++ = '\n'; + + /* Write and flush */ + len = p - c_strm->trace_jb_buf; + pj_file_write(c_strm->trace_jb_fd, c_strm->trace_jb_buf, &len); + pj_file_flush(c_strm->trace_jb_fd); + return; + +on_insuff_buffer: + pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!"); +} + +static void trace_jb_put(pjmedia_stream_common *c_strm, + const pjmedia_rtp_hdr *hdr, + unsigned payloadlen, unsigned frame_cnt) +{ + char *p = c_strm->trace_jb_buf; + char *endp = c_strm->trace_jb_buf + PJ_LOG_MAX_SIZE; + pj_ssize_t len = 0; + + if (!TRACE_JB_OPENED(c_strm)) + return; + + /* Print timestamp. */ + if (trace_jb_print_timestamp(&p, endp-p)) + goto on_insuff_buffer; + + /* Print operation, size, frame count, RTP info */ + len = pj_ansi_snprintf(p, endp-p, + "PUT,%d,%d,,%d,%d,%d,", + payloadlen, frame_cnt, + pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), hdr->m); + if ((len < 0) || (len >= endp-p)) + goto on_insuff_buffer; + p += len; + + /* Print JB state */ + if (trace_jb_print_state(c_strm, &p, endp-p)) + goto on_insuff_buffer; + + /* Print end of line */ + if (endp-p < 2) + goto on_insuff_buffer; + *p++ = '\n'; + + /* Write and flush */ + len = p - c_strm->trace_jb_buf; + pj_file_write(c_strm->trace_jb_fd, c_strm->trace_jb_buf, &len); + pj_file_flush(c_strm->trace_jb_fd); + return; + +on_insuff_buffer: + pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!"); +} + +#endif /* TRACE_JB */ + + +static pj_status_t send_rtcp(pjmedia_stream_common *c_strm, + pj_bool_t with_sdes, + pj_bool_t with_bye, + pj_bool_t with_xr, + pj_bool_t with_fb, + pj_bool_t with_fb_nack, + pj_bool_t with_fb_pli) +{ + return pjmedia_stream_send_rtcp(c_strm, with_sdes, with_bye, + with_xr, with_fb, with_fb_nack, + with_fb_pli); +} + + +#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0 +/* + * Send keep-alive packet using non-codec frame. + */ +static void send_keep_alive_packet(pjmedia_stream_common *c_strm) +{ +#if PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_EMPTY_RTP + + /* Keep-alive packet is empty RTP */ + pj_status_t status; + void *pkt; + int pkt_len; + + if (!c_strm->transport) + return; + + TRC_((c_strm->port.info.name.ptr, + "Sending keep-alive (RTCP and empty RTP)")); + + /* Send RTP */ + status = pjmedia_rtp_encode_rtp( &c_strm->enc->rtp, + c_strm->enc->pt, 0, + 1, + 0, + (const void**)&pkt, + &pkt_len); + pj_assert(status == PJ_SUCCESS); + + pj_memcpy(c_strm->enc->buf, pkt, pkt_len); + pjmedia_transport_send_rtp(c_strm->transport, c_strm->enc->buf, + pkt_len); + + /* Send RTCP */ + send_rtcp(c_strm, PJ_TRUE, PJ_FALSE, PJ_FALSE, PJ_FALSE, + PJ_FALSE, PJ_FALSE); + + /* Update stats in case the stream is paused */ + c_strm->rtcp.stat.rtp_tx_last_seq = pj_ntohs(c_strm->enc->rtp.out_hdr.seq); + +#elif PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_USER + + /* Keep-alive packet is defined in PJMEDIA_STREAM_KA_USER_PKT */ + pjmedia_channel *channel = c_strm->enc; + int pkt_len; + const pj_str_t str_ka = PJMEDIA_STREAM_KA_USER_PKT; + + TRC_((c_strm->port.info.name.ptr, + "Sending keep-alive (custom RTP/RTCP packets)")); + + /* Send to RTP port */ + pj_memcpy(c_strm->enc->buf, str_ka.ptr, str_ka.slen); + pkt_len = str_ka.slen; + pjmedia_transport_send_rtp(c_strm->transport, c_strm->enc->buf, + pkt_len); + + /* Send to RTCP port */ + pjmedia_transport_send_rtcp(c_strm->transport, c_strm->enc->buf, + pkt_len); + +#else + + PJ_UNUSED_ARG(stream); + +#endif +} +#endif /* defined(PJMEDIA_STREAM_ENABLE_KA) */ + +/** + * Publish transport error event. + */ +static void publish_tp_event(pjmedia_event_type event_type, + pj_status_t status, + pj_bool_t is_rtp, + pjmedia_dir dir, + pjmedia_stream_common *stream) +{ + pjmedia_event ev; + pj_timestamp ts_now; + + pj_get_timestamp(&ts_now); + pj_bzero(&ev.data.med_tp_err, sizeof(ev.data.med_tp_err)); + + /* Publish event. */ + pjmedia_event_init(&ev, event_type, + &ts_now, stream); + ev.data.med_tp_err.type = stream->si->type; + ev.data.med_tp_err.is_rtp = is_rtp; + ev.data.med_tp_err.dir = dir; + ev.data.med_tp_err.status = status; + + pjmedia_event_publish(NULL, stream, &ev, 0); +} + +/* + * This callback is called by stream transport on receipt of packets + * in the RTCP socket. + */ +static void on_rx_rtcp( void *data, + void *pkt, + pj_ssize_t bytes_read) +{ + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)data; + pj_status_t status; + + /* Check for errors */ + if (bytes_read < 0) { + status = (pj_status_t)-bytes_read; + if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { + return; + } + LOGERR_((c_strm->port.info.name.ptr, status, + "Unable to receive RTCP packet")); + + if (status == PJ_ESOCKETSTOP) { + /* Publish receive error event. */ + publish_tp_event(PJMEDIA_EVENT_MEDIA_TP_ERR, status, PJ_FALSE, + PJMEDIA_DIR_DECODING, c_strm); + } + return; + } + + pjmedia_rtcp_rx_rtcp(&c_strm->rtcp, pkt, bytes_read); +} + +/* + * This callback is called by stream transport on receipt of packets + * in the RTP socket. + */ +static void on_rx_rtp( pjmedia_tp_cb_param *param) +{ +#ifdef AUDIO_STREAM + pjmedia_stream *stream = (pjmedia_stream*) param->user_data; +#endif + pjmedia_stream_common *c_strm = (pjmedia_stream_common *) + param->user_data; + void *pkt = param->pkt; + pj_ssize_t bytes_read = param->size; + pjmedia_channel *channel = c_strm->dec; + const pjmedia_rtp_hdr *hdr; + const void *payload; + unsigned payloadlen; + pjmedia_rtp_status seq_st; + pj_bool_t check_pt; + pj_status_t status; + pj_bool_t pkt_discarded = PJ_FALSE; + + /* Check for errors */ + if (bytes_read < 0) { + status = (pj_status_t)-bytes_read; + if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { + return; + } + + LOGERR_((c_strm->port.info.name.ptr, status, + "Unable to receive RTP packet")); + + if (status == PJ_ESOCKETSTOP) { + /* Publish receive error event. */ + publish_tp_event(PJMEDIA_EVENT_MEDIA_TP_ERR, status, PJ_TRUE, + PJMEDIA_DIR_DECODING, c_strm); + } + return; + } + + /* Ignore non-RTP keep-alive packets */ + if (bytes_read < (pj_ssize_t) sizeof(pjmedia_rtp_hdr)) + return; + + /* Update RTP and RTCP session. */ + status = pjmedia_rtp_decode_rtp(&channel->rtp, pkt, (int)bytes_read, + &hdr, &payload, &payloadlen); + if (status != PJ_SUCCESS) { + LOGERR_((c_strm->port.info.name.ptr, status, "RTP decode error")); + c_strm->rtcp.stat.rx.discard++; + return; + } + + /* Check if multiplexing is allowed and the payload indicates RTCP. */ + if (c_strm->si->rtcp_mux && hdr->pt >= 64 && hdr->pt <= 95) { + on_rx_rtcp(c_strm, pkt, bytes_read); + return; + } + + /* See if source address of RTP packet is different than the + * configured address, and check if we need to tell the + * media transport to switch RTP remote address. + */ + if (param->src_addr) { + pj_uint32_t peer_ssrc = channel->rtp.peer_ssrc; + pj_bool_t badssrc = PJ_FALSE; + + /* Check SSRC. */ + if (!channel->rtp.has_peer_ssrc && peer_ssrc == 0) + peer_ssrc = pj_ntohl(hdr->ssrc); + + if ((c_strm->si->has_rem_ssrc) && (pj_ntohl(hdr->ssrc) != peer_ssrc)) { + badssrc = PJ_TRUE; + } + + if (pj_sockaddr_cmp(&c_strm->rem_rtp_addr, param->src_addr) == 0) { + /* We're still receiving from rem_rtp_addr. */ + c_strm->rtp_src_cnt = 0; + c_strm->rem_rtp_flag = badssrc? 2: 1; + } else { + c_strm->rtp_src_cnt++; + + if (c_strm->rtp_src_cnt < PJMEDIA_RTP_NAT_PROBATION_CNT) { + if (c_strm->rem_rtp_flag == 1 || + (c_strm->rem_rtp_flag == 2 && badssrc)) + { + /* Only discard if: + * - we have ever received packet with good ssrc from + * remote address (rem_rtp_addr), or + * - we have ever received packet with bad ssrc from + * remote address and this packet also has bad ssrc. + */ + return; + } + if (!badssrc && c_strm->rem_rtp_flag != 1) + { + /* Immediately switch if we receive packet with the + * correct ssrc AND we never receive packets with + * good ssrc from rem_rtp_addr. + */ + param->rem_switch = PJ_TRUE; + } + } else { + /* Switch. We no longer receive packets from rem_rtp_addr. */ + param->rem_switch = PJ_TRUE; + } + + if (param->rem_switch) { + /* Set remote RTP address to source address */ + pj_sockaddr_cp(&c_strm->rem_rtp_addr, param->src_addr); + + /* Reset counter and flag */ + c_strm->rtp_src_cnt = 0; + c_strm->rem_rtp_flag = badssrc? 2: 1; + + /* Update RTCP peer ssrc */ + c_strm->rtcp.peer_ssrc = pj_ntohl(hdr->ssrc); + } + } + } + + /* Add ref counter to avoid premature destroy from callbacks */ + pj_grp_lock_add_ref(c_strm->grp_lock); + + /* Ignore the packet if decoder is paused */ + if (channel->paused) + goto on_return; + + /* Update RTP session (also checks if RTP session can accept + * the incoming packet. + */ + pj_bzero(&seq_st, sizeof(seq_st)); + check_pt = PJMEDIA_STREAM_CHECK_RTP_PT; +#ifdef AUDIO_STREAM + check_pt = check_pt && hdr->pt != stream->rx_event_pt; +#endif + pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, check_pt); +#if !PJMEDIA_STREAM_CHECK_RTP_PT + if (!check_pt && hdr->pt != channel->rtp.out_pt) { +#ifdef AUDIO_STREAM + if (hdr->pt != stream->rx_event_pt) +#endif + seq_st.status.flag.badpt = -1; + } +#endif + if (seq_st.status.value) { + TRC_ ((c_strm->port.info.name.ptr, + "RTP status: badpt=%d, badssrc=%d, dup=%d, " + "outorder=%d, probation=%d, restart=%d", + seq_st.status.flag.badpt, + seq_st.status.flag.badssrc, + seq_st.status.flag.dup, + seq_st.status.flag.outorder, + seq_st.status.flag.probation, + seq_st.status.flag.restart)); + + if (seq_st.status.flag.badpt) { + PJ_LOG(4,(c_strm->port.info.name.ptr, + "Bad RTP pt %d (expecting %d)", + hdr->pt, channel->rtp.out_pt)); + } + + if (!c_strm->si->has_rem_ssrc && seq_st.status.flag.badssrc) { + PJ_LOG(4,(c_strm->port.info.name.ptr, + "Changed RTP peer SSRC %d (previously %d)", + channel->rtp.peer_ssrc, c_strm->rtcp.peer_ssrc)); + c_strm->rtcp.peer_ssrc = channel->rtp.peer_ssrc; + } + + + } + + /* Skip bad RTP packet */ + if (seq_st.status.flag.bad) { + pkt_discarded = PJ_TRUE; + goto on_return; + } + + /* Ignore if payloadlen is zero */ + if (payloadlen == 0) { + pkt_discarded = PJ_TRUE; + goto on_return; + } + + /* Pass it to specific stream for further processing. */ + on_stream_rx_rtp(c_strm, hdr, payload, payloadlen, seq_st, + &pkt_discarded); + +on_return: + /* Update RTCP session */ + if (c_strm->rtcp.peer_ssrc == 0) + c_strm->rtcp.peer_ssrc = channel->rtp.peer_ssrc; + + pjmedia_rtcp_rx_rtp2(&c_strm->rtcp, pj_ntohs(hdr->seq), + pj_ntohl(hdr->ts), payloadlen, pkt_discarded); + + /* RTCP-FB generic NACK */ + if (c_strm->rtcp.received >= 10 && seq_st.diff > 1 && + c_strm->send_rtcp_fb_nack && pj_ntohs(hdr->seq) >= seq_st.diff) + { + pj_uint16_t nlost, first_seq; + + /* Report only one NACK (last 17 losts) */ + nlost = PJ_MIN(seq_st.diff - 1, 17); + first_seq = pj_ntohs(hdr->seq) - nlost; + + pj_bzero(&c_strm->rtcp_fb_nack, sizeof(c_strm->rtcp_fb_nack)); + c_strm->rtcp_fb_nack.pid = first_seq; + while (--nlost) { + c_strm->rtcp_fb_nack.blp <<= 1; + c_strm->rtcp_fb_nack.blp |= 1; + } + + /* Send it immediately */ + status = send_rtcp(c_strm, !c_strm->rtcp_sdes_bye_disabled, + PJ_FALSE, PJ_FALSE, PJ_TRUE, PJ_FALSE, PJ_FALSE); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(c_strm->port.info.name.ptr, status, + "Error sending RTCP FB generic NACK")); + } else { + c_strm->initial_rr = PJ_TRUE; + } + } + + /* Send RTCP RR and SDES after we receive some RTP packets */ + if (c_strm->rtcp.received >= 10 && !c_strm->initial_rr) { + status = send_rtcp(c_strm, !c_strm->rtcp_sdes_bye_disabled, + PJ_FALSE, PJ_FALSE, PJ_FALSE, PJ_FALSE, PJ_FALSE); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(c_strm->port.info.name.ptr, status, + "Error sending initial RTCP RR")); + } else { + c_strm->initial_rr = PJ_TRUE; + } + } + + pj_grp_lock_dec_ref(c_strm->grp_lock); +} + +/* Common stream destroy handler. */ +static void on_destroy(void *arg) +{ + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)arg; + + /* This function may be called when stream is partly initialized. */ + + /* Call specific stream destroy handler. */ + on_stream_destroy(arg); + + /* Release ref to transport */ + if (c_strm->transport && c_strm->transport->grp_lock) + pj_grp_lock_dec_ref(c_strm->transport->grp_lock); + + /* Free mutex */ + if (c_strm->jb_mutex) { + pj_mutex_destroy(c_strm->jb_mutex); + c_strm->jb_mutex = NULL; + } + + /* Destroy jitter buffer */ + if (c_strm->jb) { + pjmedia_jbuf_destroy(c_strm->jb); + c_strm->jb = NULL; + } + +#if TRACE_JB + if (TRACE_JB_OPENED(c_strm)) { + pj_file_close(c_strm->trace_jb_fd); + c_strm->trace_jb_fd = TRACE_JB_INVALID_FD; + } +#endif + + PJ_LOG(4,(c_strm->port.info.name.ptr, "Stream destroyed")); + pj_pool_safe_release(&c_strm->own_pool); +} diff --git a/pjmedia/src/pjmedia/stream_info.c b/pjmedia/src/pjmedia/stream_info.c index 0a474a6e50..f6f5f674f2 100644 --- a/pjmedia/src/pjmedia/stream_info.c +++ b/pjmedia/src/pjmedia/stream_info.c @@ -22,10 +22,6 @@ #include #include -static const pj_str_t ID_IN = { "IN", 2 }; -static const pj_str_t ID_IP4 = { "IP4", 3}; -static const pj_str_t ID_IP6 = { "IP6", 3}; -//static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 }; static const pj_str_t ID_RTPMAP = { "rtpmap", 6 }; static const pj_str_t ID_TELEPHONE_EVENT = { "telephone-event", 15 }; @@ -357,38 +353,25 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( const pjmedia_sdp_session *remote, unsigned stream_idx) { - const pj_str_t STR_INACTIVE = { "inactive", 8 }; - const pj_str_t STR_SENDONLY = { "sendonly", 8 }; - const pj_str_t STR_RECVONLY = { "recvonly", 8 }; - + pjmedia_stream_info_common *csi = (pjmedia_stream_info_common *)si; pjmedia_codec_mgr *mgr; - const pjmedia_sdp_attr *attr; const pjmedia_sdp_media *local_m; const pjmedia_sdp_media *rem_m; - const pjmedia_sdp_conn *local_conn; - const pjmedia_sdp_conn *rem_conn; - int rem_af, local_af; - unsigned i; + pj_bool_t active; pj_status_t status; + PJ_ASSERT_RETURN(si, PJ_EINVAL); + pj_bzero(si, sizeof(*si)); - /* Validate arguments: */ - PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); - PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); - PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); + status = pjmedia_stream_info_common_from_sdp(csi, pool, endpt, local, + remote, stream_idx, &active); + if (status != PJ_SUCCESS || !active) + return status; /* Keep SDP shortcuts */ local_m = local->media[stream_idx]; rem_m = remote->media[stream_idx]; - local_conn = local_m->conn ? local_m->conn : local->conn; - if (local_conn == NULL) - return PJMEDIA_SDP_EMISSINGCONN; - - rem_conn = rem_m->conn ? rem_m->conn : remote->conn; - if (rem_conn == NULL) - return PJMEDIA_SDP_EMISSINGCONN; - /* Media type must be audio */ if (pjmedia_get_type(&local_m->desc.media) != PJMEDIA_TYPE_AUDIO) return PJMEDIA_EINVALIMEDIATYPE; @@ -396,193 +379,6 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( /* Get codec manager. */ mgr = pjmedia_endpt_get_codec_mgr(endpt); - /* Reset: */ - - pj_bzero(si, sizeof(*si)); - -#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR - /* Set default RTCP XR enabled/disabled */ - si->rtcp_xr_enabled = PJ_TRUE; -#endif - - /* Media type: */ - si->type = PJMEDIA_TYPE_AUDIO; - - /* Transport protocol */ - - /* At this point, transport type must be compatible, - * the transport instance will do more validation later. - */ - status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, - &local_m->desc.transport); - if (status != PJ_SUCCESS) - return PJMEDIA_SDPNEG_EINVANSTP; - - /* Get the transport protocol */ - si->proto = pjmedia_sdp_transport_get_proto(&local_m->desc.transport); - - /* Just return success if stream is not RTP/AVP compatible */ - if (!PJMEDIA_TP_PROTO_HAS_FLAG(si->proto, PJMEDIA_TP_PROTO_RTP_AVP)) - return PJ_SUCCESS; - - /* Check address family in remote SDP */ - rem_af = pj_AF_UNSPEC(); - if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) { - if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) { - rem_af = pj_AF_INET(); - } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) { - rem_af = pj_AF_INET6(); - } - } - - if (rem_af==pj_AF_UNSPEC()) { - /* Unsupported address family */ - return PJ_EAFNOTSUP; - } - - /* Set remote address: */ - status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, - rem_m->desc.port); - if (status == PJ_ERESOLVE && rem_af == pj_AF_INET()) { - /* Handle special case in NAT64 scenario where for some reason, server - * puts IPv6 (literal or FQDN) in SDP answer while indicating "IP4" - * in its address type, let's retry resolving using AF_INET6. - */ - status = pj_sockaddr_init(pj_AF_INET6(), &si->rem_addr, - &rem_conn->addr, rem_m->desc.port); - } - if (status != PJ_SUCCESS) { - /* Invalid IP address. */ - return PJMEDIA_EINVALIDIP; - } - - /* Check address family of local info */ - local_af = pj_AF_UNSPEC(); - if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) { - if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) { - local_af = pj_AF_INET(); - } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) { - local_af = pj_AF_INET6(); - } - } - - if (local_af==pj_AF_UNSPEC()) { - /* Unsupported address family */ - return PJ_SUCCESS; - } - - /* Set remote address: */ - status = pj_sockaddr_init(local_af, &si->local_addr, &local_conn->addr, - local_m->desc.port); - if (status != PJ_SUCCESS) { - /* Invalid IP address. */ - return PJMEDIA_EINVALIDIP; - } - - /* Local and remote address family must match, except when ICE is used - * by both sides (see also ticket #1952). - */ - if (local_af != rem_af) { - const pj_str_t STR_ICE_CAND = { "candidate", 9 }; - if (pjmedia_sdp_media_find_attr(rem_m, &STR_ICE_CAND, NULL)==NULL || - pjmedia_sdp_media_find_attr(local_m, &STR_ICE_CAND, NULL)==NULL) - { - return PJ_EAFNOTSUP; - } - } - - /* Media direction: */ - - if (local_m->desc.port == 0 || - pj_sockaddr_has_addr(&si->local_addr)==PJ_FALSE || - pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE || - pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) - { - /* Inactive stream. */ - - si->dir = PJMEDIA_DIR_NONE; - - } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { - - /* Send only stream. */ - - si->dir = PJMEDIA_DIR_ENCODING; - - } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { - - /* Recv only stream. */ - - si->dir = PJMEDIA_DIR_DECODING; - - } else { - - /* Send and receive stream. */ - - si->dir = PJMEDIA_DIR_ENCODING_DECODING; - - } - - /* No need to do anything else if stream is rejected */ - if (local_m->desc.port == 0) { - return PJ_SUCCESS; - } - - /* Check if "rtcp-mux" is present in the SDP. */ - attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, - "rtcp-mux", NULL); - if (attr) - si->rtcp_mux = PJ_TRUE; - - /* If "rtcp" attribute is present in the SDP, set the RTCP address - * from that attribute. Otherwise, calculate from RTP address. - */ - attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, - "rtcp", NULL); - if (attr) { - pjmedia_sdp_rtcp_attr rtcp; - status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); - if (status == PJ_SUCCESS) { - if (rtcp.addr.slen) { - status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr, - (pj_uint16_t)rtcp.port); - if (status != PJ_SUCCESS) - return PJMEDIA_EINVALIDIP; - } else { - pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, - (pj_uint16_t)rtcp.port); - pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp), - pj_sockaddr_get_addr(&si->rem_addr), - pj_sockaddr_get_addr_len(&si->rem_addr)); - } - } - } - - if (!pj_sockaddr_has_addr(&si->rem_rtcp)) { - int rtcp_port; - - pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr)); - rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1; - pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port); - } - - /* Check if "ssrc" attribute is present in the SDP. */ - for (i = 0; i < rem_m->attr_count; i++) { - if (pj_strcmp2(&rem_m->attr[i]->name, "ssrc") == 0) { - pjmedia_sdp_ssrc_attr ssrc; - - status = pjmedia_sdp_attr_get_ssrc( - (const pjmedia_sdp_attr *)rem_m->attr[i], &ssrc); - if (status == PJ_SUCCESS) { - si->has_rem_ssrc = PJ_TRUE; - si->rem_ssrc = ssrc.ssrc; - if (ssrc.cname.slen > 0) { - pj_strdup(pool, &si->rem_cname, &ssrc.cname); - break; - } - } - } - } - /* Get the payload number for receive channel. */ /* Previously we used to rely on fmt[0] being the selected codec, @@ -605,27 +401,6 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( /* Get codec info and param */ status = get_audio_codec_info_param(si, pool, mgr, local_m, rem_m); - if (status != PJ_SUCCESS) - return status; - - /* Leave SSRC to random. */ - si->ssrc = pj_rand(); - - /* Set default jitter buffer parameter. */ - si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; - si->jb_discard_algo = PJMEDIA_JB_DISCARD_PROGRESSIVE; - - /* Get local RTCP-FB info */ - status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, local, stream_idx, - si->rx_pt, &si->loc_rtcp_fb); - if (status != PJ_SUCCESS) - return status; - - /* Get remote RTCP-FB info */ - status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, remote, stream_idx, - si->tx_pt, &si->rem_rtcp_fb); - if (status != PJ_SUCCESS) - return status; return status; } diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c index f3a74b85ff..c5ce745a3b 100644 --- a/pjmedia/src/pjmedia/vid_stream.c +++ b/pjmedia/src/pjmedia/vid_stream.c @@ -46,18 +46,10 @@ #define TRACE_RC 0 -/* Tracing jitter buffer operations in a stream session to a CSV file. - * The trace will contain JB operation timestamp, frame info, RTP info, and - * the JB state right after the operation. - */ -#define TRACE_JB 0 /* Enable/disable trace. */ -#define TRACE_JB_PATH_PREFIX "" /* Optional path/prefix - for the CSV filename. */ -#if TRACE_JB -# include -# define TRACE_JB_INVALID_FD ((pj_oshandle_t)-1) -# define TRACE_JB_OPENED(s) (s->trace_jb_fd != TRACE_JB_INVALID_FD) -#endif +/* Enable/disable trace. */ +#define TRACE_JB PJMEDIA_STREAM_TRACE_JB +/* Optional path/prefix for the CSV filename. */ +#define TRACE_JB_PATH_PREFIX "" #ifndef PJMEDIA_VSTREAM_SIZE # define PJMEDIA_VSTREAM_SIZE 16000 @@ -80,18 +72,7 @@ /** * Media channel. */ -typedef struct pjmedia_vid_channel -{ - pjmedia_vid_stream *stream; /**< Parent stream. */ - pjmedia_dir dir; /**< Channel direction. */ - pjmedia_port port; /**< Port interface. */ - unsigned pt; /**< Payload type. */ - pj_bool_t paused; /**< Paused?. */ - void *buf; /**< Output buffer. */ - unsigned buf_size; /**< Size of output buffer. */ - pjmedia_rtp_session rtp; /**< RTP session. */ -} pjmedia_vid_channel; - +typedef pjmedia_channel pjmedia_vid_channel; /** * This structure describes media stream. @@ -102,35 +83,12 @@ typedef struct pjmedia_vid_channel */ struct pjmedia_vid_stream { - pj_pool_t *own_pool; /**< Internal pool. */ - pjmedia_endpt *endpt; /**< Media endpoint. */ + pjmedia_stream_common base; + pjmedia_vid_codec_mgr *codec_mgr; /**< Codec manager. */ pjmedia_vid_stream_info info; /**< Stream info. */ - pj_grp_lock_t *grp_lock; /**< Stream lock. */ - - pjmedia_vid_channel *enc; /**< Encoding channel. */ - pjmedia_vid_channel *dec; /**< Decoding channel. */ - - pjmedia_dir dir; /**< Stream direction. */ - void *user_data; /**< User data. */ - pj_str_t name; /**< Stream name */ - pj_str_t cname; /**< SDES CNAME */ - pjmedia_transport *transport; /**< Stream transport. */ - - pjmedia_jbuf *jb; /**< Jitter buffer. */ - char jb_last_frm; /**< Last frame type from jb */ - unsigned jb_last_frm_cnt;/**< Last JB frame type counter*/ - - pjmedia_rtcp_session rtcp; /**< RTCP for incoming RTP. */ pj_timestamp rtcp_last_tx; /**< Last RTCP tx time. */ - pj_timestamp rtcp_fb_last_tx;/**< Last RTCP-FB tx time. */ - pj_uint32_t rtcp_interval; /**< Interval, in msec. */ - pj_bool_t initial_rr; /**< Initial RTCP RR sent */ - pj_bool_t rtcp_sdes_bye_disabled;/**< Send RTCP SDES/BYE?*/ - void *out_rtcp_pkt; /**< Outgoing RTCP packet. */ - unsigned out_rtcp_pkt_size; - /**< Outgoing RTCP packet size. */ unsigned dec_max_size; /**< Size of decoded/raw picture*/ pjmedia_ratio dec_max_fps; /**< Max fps of decoding dir. */ @@ -157,55 +115,13 @@ struct pjmedia_vid_stream /**< Timestamp of the last keyframe. */ - -#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 - pj_bool_t use_ka; /**< Stream keep-alive with non- - codec-VAD mechanism is - enabled? */ - unsigned ka_interval; /**< The keepalive sending - interval */ - pj_time_val last_frm_ts_sent; /**< Time of last sending - packet */ - unsigned start_ka_count; /**< The number of keep-alive - to be sent after it is - created */ - unsigned start_ka_interval;/**< The keepalive sending - interval after the stream - is created */ - pj_timestamp last_start_ka_tx; /**< Timestamp of the last - keepalive sent */ -#endif - -#if TRACE_JB - pj_oshandle_t trace_jb_fd; /**< Jitter tracing file handle.*/ - char *trace_jb_buf; /**< Jitter tracing buffer. */ -#endif - pjmedia_vid_codec *codec; /**< Codec instance being used. */ pj_uint32_t last_dec_ts; /**< Last decoded timestamp. */ int last_dec_seq; /**< Last decoded sequence. */ - pj_uint32_t rtp_tx_err_cnt;/**< The number of RTP - send() error */ - pj_uint32_t rtcp_tx_err_cnt;/**< The number of RTCP - send() error */ pj_timestamp ts_freq; /**< Timestamp frequency. */ - pj_sockaddr rem_rtp_addr; /**< Remote RTP address */ - unsigned rem_rtp_flag; /**< Indicator flag about - packet from this addr. - 0=no pkt, 1=good ssrc pkts, - 2=bad ssrc pkts */ - pj_sockaddr rtp_src_addr; /**< Actual packet src addr. */ - unsigned rtp_src_cnt; /**< How many pkt from this addr*/ - - /* RTCP Feedback */ - pj_bool_t send_rtcp_fb_nack; /**< Send NACK? */ - int pending_rtcp_fb_nack; /**< Any pending NACK? */ - int rtcp_fb_nack_cap_idx; /**< RX NACK cap idx. */ - pjmedia_rtcp_fb_nack rtcp_fb_nack; /**< TX NACK state. */ - pj_bool_t send_rtcp_fb_pli; /**< Send PLI? */ int pending_rtcp_fb_pli; /**< Any pending PLI? */ int rtcp_fb_pli_cap_idx; /**< RX PLI cap idx. */ @@ -223,13 +139,6 @@ struct pjmedia_vid_stream static pj_status_t decode_frame(pjmedia_vid_stream *stream, pjmedia_frame *frame); - -static pj_status_t send_rtcp(pjmedia_vid_stream *stream, - pj_bool_t with_sdes, - pj_bool_t with_bye, - pj_bool_t with_fb_nack, - pj_bool_t with_fb_pli); - static void on_rx_rtcp( void *data, void *pkt, pj_ssize_t bytes_read); @@ -237,155 +146,7 @@ static void on_rx_rtcp( void *data, static void on_destroy(void *arg); -#if TRACE_JB - -PJ_INLINE(int) trace_jb_print_timestamp(char **buf, pj_ssize_t len) -{ - pj_time_val now; - pj_parsed_time ptime; - char *p = *buf; - - if (len < 14) - return -1; - - pj_gettimeofday(&now); - pj_time_decode(&now, &ptime); - p += pj_utoa_pad(ptime.hour, p, 2, '0'); - *p++ = ':'; - p += pj_utoa_pad(ptime.min, p, 2, '0'); - *p++ = ':'; - p += pj_utoa_pad(ptime.sec, p, 2, '0'); - *p++ = '.'; - p += pj_utoa_pad(ptime.msec, p, 3, '0'); - *p++ = ','; - - *buf = p; - - return 0; -} - -PJ_INLINE(int) trace_jb_print_state(pjmedia_vid_stream *stream, - char **buf, pj_ssize_t len) -{ - char *p = *buf; - char *endp = *buf + len; - pjmedia_jb_state state; - - pjmedia_jbuf_get_state(stream->jb, &state); - - len = pj_ansi_snprintf(p, endp-p, "%d, %d, %d", - state.size, state.burst, state.prefetch); - if ((len < 0) || (len >= endp-p)) - return -1; - - p += len; - *buf = p; - return 0; -} - -static void trace_jb_get(pjmedia_vid_stream *stream, pjmedia_jb_frame_type ft, - pj_size_t fsize) -{ - char *p = stream->trace_jb_buf; - char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE; - pj_ssize_t len = 0; - const char* ft_st; - - if (!TRACE_JB_OPENED(stream)) - return; - - /* Print timestamp. */ - if (trace_jb_print_timestamp(&p, endp-p)) - goto on_insuff_buffer; - - /* Print frame type and size */ - switch(ft) { - case PJMEDIA_JB_MISSING_FRAME: - ft_st = "missing"; - break; - case PJMEDIA_JB_NORMAL_FRAME: - ft_st = "normal"; - break; - case PJMEDIA_JB_ZERO_PREFETCH_FRAME: - ft_st = "prefetch"; - break; - case PJMEDIA_JB_ZERO_EMPTY_FRAME: - ft_st = "empty"; - break; - default: - ft_st = "unknown"; - break; - } - - /* Print operation, size, frame count, frame type */ - len = pj_ansi_snprintf(p, endp-p, "GET,%d,1,%s,,,,", fsize, ft_st); - if ((len < 0) || (len >= endp-p)) - goto on_insuff_buffer; - p += len; - - /* Print JB state */ - if (trace_jb_print_state(stream, &p, endp-p)) - goto on_insuff_buffer; - - /* Print end of line */ - if (endp-p < 2) - goto on_insuff_buffer; - *p++ = '\n'; - - /* Write and flush */ - len = p - stream->trace_jb_buf; - pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); - pj_file_flush(stream->trace_jb_fd); - return; - -on_insuff_buffer: - pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!"); -} - -static void trace_jb_put(pjmedia_vid_stream *stream, - const pjmedia_rtp_hdr *hdr, - unsigned payloadlen, unsigned frame_cnt) -{ - char *p = stream->trace_jb_buf; - char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE; - pj_ssize_t len = 0; - - if (!TRACE_JB_OPENED(stream)) - return; - - /* Print timestamp. */ - if (trace_jb_print_timestamp(&p, endp-p)) - goto on_insuff_buffer; - - /* Print operation, size, frame count, RTP info */ - len = pj_ansi_snprintf(p, endp-p, - "PUT,%d,%d,,%d,%d,%d,", - payloadlen, frame_cnt, - pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), hdr->m); - if ((len < 0) || (len >= endp-p)) - goto on_insuff_buffer; - p += len; - - /* Print JB state */ - if (trace_jb_print_state(stream, &p, endp-p)) - goto on_insuff_buffer; - - /* Print end of line */ - if (endp-p < 2) - goto on_insuff_buffer; - *p++ = '\n'; - - /* Write and flush */ - len = p - stream->trace_jb_buf; - pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); - pj_file_flush(stream->trace_jb_fd); - return; - -on_insuff_buffer: - pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!"); -} - -#endif /* TRACE_JB */ +#include "stream_imp_common.c" static void dump_port_info(const pjmedia_vid_channel *chan, const char *event_name) @@ -411,6 +172,7 @@ static pj_status_t stream_event_cb(pjmedia_event *event, void *user_data) { pjmedia_vid_stream *stream = (pjmedia_vid_stream*)user_data; + pjmedia_stream_common *c_strm = &stream->base; if (event->epub == stream->codec) { /* This is codec event */ @@ -439,7 +201,7 @@ static pj_status_t stream_event_cb(pjmedia_event *event, default: break; } - } else if (event->epub == &stream->rtcp && + } else if (event->epub == &c_strm->rtcp && event->type==PJMEDIA_EVENT_RX_RTCP_FB) { /* This is RX RTCP-FB event */ @@ -449,13 +211,13 @@ static pj_status_t stream_event_cb(pjmedia_event *event, /* Check if configured to listen to the RTCP-FB type */ if (data->cap.type == PJMEDIA_RTCP_FB_NACK) { if (data->cap.param.slen == 0 && - stream->rtcp_fb_nack_cap_idx >= 0) + c_strm->rtcp_fb_nack_cap_idx >= 0) { /* Generic NACK */ /* Update event data capability before republishing */ data->cap = stream->info.loc_rtcp_fb.caps[ - stream->rtcp_fb_nack_cap_idx]; + c_strm->rtcp_fb_nack_cap_idx]; } else if (pj_strcmp2(&data->cap.param, "pli") == 0 && stream->rtcp_fb_pli_cap_idx >= 0) @@ -478,207 +240,6 @@ static pj_status_t stream_event_cb(pjmedia_event *event, PJMEDIA_EVENT_PUBLISH_POST_EVENT); } - -/** - * Publish transport error event. - */ -static void publish_tp_event(pjmedia_event_type event_type, - pj_status_t status, - pj_bool_t is_rtp, - pjmedia_dir dir, - pjmedia_vid_stream *stream) -{ - pjmedia_event ev; - pj_timestamp ts_now; - - pj_get_timestamp(&ts_now); - pj_bzero(&ev.data.med_tp_err, sizeof(ev.data.med_tp_err)); - - /* Publish event. */ - pjmedia_event_init(&ev, event_type, - &ts_now, stream); - ev.data.med_tp_err.type = PJMEDIA_TYPE_VIDEO; - ev.data.med_tp_err.is_rtp = is_rtp; - ev.data.med_tp_err.dir = dir; - ev.data.med_tp_err.status = status; - - pjmedia_event_publish(NULL, stream, &ev, 0); -} - -#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0 -/* - * Send keep-alive packet using non-codec frame. - */ -static void send_keep_alive_packet(pjmedia_vid_stream *stream) -{ -#if PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_EMPTY_RTP - - /* Keep-alive packet is empty RTP */ - pjmedia_vid_channel *channel = stream->enc; - pj_status_t status; - void *pkt; - int pkt_len; - - if (!stream->transport) - return; - - TRC_((channel->port.info.name.ptr, - "Sending keep-alive (RTCP and empty RTP)")); - - /* Send RTP */ - status = pjmedia_rtp_encode_rtp( &stream->enc->rtp, - stream->enc->pt, 0, - 1, - 0, - (const void**)&pkt, - &pkt_len); - pj_assert(status == PJ_SUCCESS); - - pj_memcpy(stream->enc->buf, pkt, pkt_len); - pjmedia_transport_send_rtp(stream->transport, stream->enc->buf, - pkt_len); - - /* Send RTCP */ - send_rtcp(stream, PJ_TRUE, PJ_FALSE, PJ_FALSE, PJ_FALSE); - - /* Update stats in case the stream is paused */ - stream->rtcp.stat.rtp_tx_last_seq = pj_ntohs(stream->enc->rtp.out_hdr.seq); - -#elif PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_USER - - /* Keep-alive packet is defined in PJMEDIA_STREAM_KA_USER_PKT */ - pjmedia_vid_channel *channel = stream->enc; - int pkt_len; - const pj_str_t str_ka = PJMEDIA_STREAM_KA_USER_PKT; - - TRC_((channel->port.info.name.ptr, - "Sending keep-alive (custom RTP/RTCP packets)")); - - /* Send to RTP port */ - pj_memcpy(stream->enc->buf, str_ka.ptr, str_ka.slen); - pkt_len = str_ka.slen; - pjmedia_transport_send_rtp(stream->transport, stream->enc->buf, - pkt_len); - - /* Send to RTCP port */ - pjmedia_transport_send_rtcp(stream->transport, stream->enc->buf, - pkt_len); - -#else - - PJ_UNUSED_ARG(stream); - -#endif -} -#endif /* defined(PJMEDIA_STREAM_ENABLE_KA) */ - - -static pj_status_t send_rtcp(pjmedia_vid_stream *stream, - pj_bool_t with_sdes, - pj_bool_t with_bye, - pj_bool_t with_fb_nack, - pj_bool_t with_fb_pli) -{ - void *sr_rr_pkt; - pj_uint8_t *pkt; - int len, max_len; - pj_status_t status; - - - /* To avoid deadlock with media transport, we use the transport's - * group lock. - */ - if (stream->transport->grp_lock) - pj_grp_lock_acquire( stream->transport->grp_lock ); - - /* Build RTCP RR/SR packet */ - pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len); - - if (with_sdes || with_bye || with_fb_nack || with_fb_pli) { - pkt = (pj_uint8_t*) stream->out_rtcp_pkt; - pj_memcpy(pkt, sr_rr_pkt, len); - max_len = stream->out_rtcp_pkt_size; - } else { - pkt = (pj_uint8_t*)sr_rr_pkt; - max_len = len; - } - - /* Build RTCP SDES packet, forced if also send RTCP-FB */ - with_sdes = with_sdes || with_fb_pli || with_fb_nack; - if (with_sdes) { - pjmedia_rtcp_sdes sdes; - pj_size_t sdes_len; - - pj_bzero(&sdes, sizeof(sdes)); - sdes.cname = stream->cname; - sdes_len = max_len - len; - status = pjmedia_rtcp_build_rtcp_sdes(&stream->rtcp, pkt+len, - &sdes_len, &sdes); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->name.ptr, status, - "Error generating RTCP SDES")); - } else { - len += (int)sdes_len; - } - } - - /* Build RTCP BYE packet */ - if (with_bye) { - pj_size_t bye_len = max_len - len; - status = pjmedia_rtcp_build_rtcp_bye(&stream->rtcp, pkt+len, - &bye_len, NULL); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->name.ptr, status, - "Error generating RTCP BYE")); - } else { - len += (int)bye_len; - } - } - - /* Build RTCP-FB generic NACK packet */ - if (with_fb_nack && stream->rtcp_fb_nack.pid >= 0) { - pj_size_t fb_len = max_len - len; - status = pjmedia_rtcp_fb_build_nack(&stream->rtcp, pkt+len, &fb_len, - 1, &stream->rtcp_fb_nack); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->name.ptr, status, - "Error generating RTCP-FB NACK")); - } else { - len += (int)fb_len; - } - } - - /* Build RTCP-FB PLI packet */ - if (with_fb_pli) { - pj_size_t fb_len = max_len - len; - status = pjmedia_rtcp_fb_build_pli(&stream->rtcp, pkt+len, &fb_len); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->name.ptr, status, - "Error generating RTCP-FB PLI")); - } else { - len += (int)fb_len; - PJ_LOG(5,(stream->name.ptr, "Sending RTCP-FB PLI packet")); - } - } - - /* Send! */ - status = pjmedia_transport_send_rtcp(stream->transport, pkt, len); - if (status != PJ_SUCCESS) { - if (stream->rtcp_tx_err_cnt++ == 0) { - LOGERR_((stream->name.ptr, status, "Error sending RTCP")); - } - if (stream->rtcp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) { - stream->rtcp_tx_err_cnt = 0; - } - } - - if (stream->transport->grp_lock) - pj_grp_lock_release( stream->transport->grp_lock ); - - return status; -} - - /** * check_tx_rtcp() * @@ -688,6 +249,7 @@ static pj_status_t send_rtcp(pjmedia_vid_stream *stream, */ static void check_tx_rtcp(pjmedia_vid_stream *stream) { + pjmedia_stream_common *c_strm = &stream->base; pj_timestamp now; pj_bool_t early; @@ -695,10 +257,10 @@ static void check_tx_rtcp(pjmedia_vid_stream *stream) * elapsed timestamp from previous RTCP-FB >= PJMEDIA_RTCP_FB_INTERVAL). */ pj_get_timestamp(&now); - early = ((stream->pending_rtcp_fb_pli || stream->pending_rtcp_fb_nack) + early = ((stream->pending_rtcp_fb_pli || c_strm->pending_rtcp_fb_nack) && - (stream->rtcp_fb_last_tx.u64 == 0 || - pj_elapsed_msec(&stream->rtcp_fb_last_tx, &now) >= + (c_strm->rtcp_fb_last_tx.u64 == 0 || + pj_elapsed_msec(&c_strm->rtcp_fb_last_tx, &now) >= PJMEDIA_RTCP_FB_INTERVAL)); /* First check, unless RTCP is 'urgent', just init rtcp_last_tx. */ @@ -709,226 +271,49 @@ static void check_tx_rtcp(pjmedia_vid_stream *stream) /* Build & send RTCP */ if (early || - pj_elapsed_msec(&stream->rtcp_last_tx, &now) >= stream->rtcp_interval) + pj_elapsed_msec(&stream->rtcp_last_tx, &now) >= c_strm->rtcp_interval) { pj_status_t status; - status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, PJ_FALSE, - stream->pending_rtcp_fb_nack, + status = send_rtcp(c_strm, !c_strm->rtcp_sdes_bye_disabled, PJ_FALSE, + PJ_FALSE, PJ_FALSE, c_strm->pending_rtcp_fb_nack, stream->pending_rtcp_fb_pli); if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->name.ptr, status, + PJ_PERROR(4,(c_strm->name.ptr, status, "Error sending RTCP")); } stream->rtcp_last_tx = now; if (early) - stream->rtcp_fb_last_tx = now; + c_strm->rtcp_fb_last_tx = now; if (stream->pending_rtcp_fb_pli) stream->pending_rtcp_fb_pli--; - if (stream->pending_rtcp_fb_nack) - stream->pending_rtcp_fb_nack--; - } -} - - -#if 0 -static void dump_bin(const char *buf, unsigned len) -{ - unsigned i; - - PJ_LOG(3,(THIS_FILE, "begin dump")); - for (i=0; ipending_rtcp_fb_nack) + c_strm->pending_rtcp_fb_nack--; } - PJ_LOG(3,(THIS_FILE, "end dump")); } -#endif - /* - * This callback is called by stream transport on receipt of packets - * in the RTP socket. + * This callback is called by common stream processing on receipt of + * packets in the RTP socket (i.e. called by on_rx_rtp() in + * stream_imp_common.c) */ -static void on_rx_rtp( pjmedia_tp_cb_param *param) +static pj_status_t on_stream_rx_rtp(pjmedia_stream_common *c_strm, + const pjmedia_rtp_hdr *hdr, + const void *payload, + unsigned payloadlen, + pjmedia_rtp_status seq_st, + pj_bool_t *pkt_discarded) { - pjmedia_vid_stream *stream = (pjmedia_vid_stream*) param->user_data; - void *pkt = param->pkt; - pj_ssize_t bytes_read = param->size; - pjmedia_vid_channel *channel = stream->dec; - const pjmedia_rtp_hdr *hdr; - const void *payload; - unsigned payloadlen; - pjmedia_rtp_status seq_st; - pj_status_t status; + pjmedia_vid_stream *stream = (pjmedia_vid_stream*) c_strm; + pjmedia_vid_channel *channel = c_strm->dec; + pj_status_t status = PJ_SUCCESS; long ts_diff; - pj_bool_t pkt_discarded = PJ_FALSE; - - /* Check for errors */ - if (bytes_read < 0) { - status = (pj_status_t)-bytes_read; - if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { - return; - } - - LOGERR_((channel->port.info.name.ptr, status, - "Unable to receive RTP packet")); - if (status == PJ_ESOCKETSTOP) { - /* Publish receive error event. */ - publish_tp_event(PJMEDIA_EVENT_MEDIA_TP_ERR, status, PJ_TRUE, - PJMEDIA_DIR_DECODING, stream); - } - return; - } - - /* Ignore keep-alive packets */ - if (bytes_read < (pj_ssize_t) sizeof(pjmedia_rtp_hdr)) - return; - - /* Update RTP and RTCP session. */ - status = pjmedia_rtp_decode_rtp(&channel->rtp, pkt, (int)bytes_read, - &hdr, &payload, &payloadlen); - if (status != PJ_SUCCESS) { - LOGERR_((channel->port.info.name.ptr, status, "RTP decode error")); - stream->rtcp.stat.rx.discard++; - return; - } - - /* Check if multiplexing is allowed and the payload indicates RTCP. */ - if (stream->info.rtcp_mux && hdr->pt >= 64 && hdr->pt <= 95) { - on_rx_rtcp(stream, pkt, bytes_read); - return; - } - - /* Add ref counter to avoid premature destroy from callbacks */ - pj_grp_lock_add_ref(stream->grp_lock); - - /* Ignore the packet if decoder is paused */ - if (channel->paused) - goto on_return; - - /* Update RTP session (also checks if RTP session can accept - * the incoming packet. - */ - pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, - PJMEDIA_VID_STREAM_CHECK_RTP_PT); -#if !PJMEDIA_VID_STREAM_CHECK_RTP_PT - if (hdr->pt != channel->rtp.out_pt) { - seq_st.status.flag.badpt = -1; - } -#endif - if (seq_st.status.value) { - TRC_ ((channel->port.info.name.ptr, - "RTP status: badpt=%d, badssrc=%d, dup=%d, " - "outorder=%d, probation=%d, restart=%d", - seq_st.status.flag.badpt, - seq_st.status.flag.badssrc, - seq_st.status.flag.dup, - seq_st.status.flag.outorder, - seq_st.status.flag.probation, - seq_st.status.flag.restart)); - - if (seq_st.status.flag.badpt) { - PJ_LOG(4,(channel->port.info.name.ptr, - "Bad RTP pt %d (expecting %d)", - hdr->pt, channel->rtp.out_pt)); - } - - if (!stream->info.has_rem_ssrc && seq_st.status.flag.badssrc) { - PJ_LOG(4,(channel->port.info.name.ptr, - "Changed RTP peer SSRC %d (previously %d)", - channel->rtp.peer_ssrc, stream->rtcp.peer_ssrc)); - stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc; - } - - - } - - /* Skip bad RTP packet */ - if (seq_st.status.flag.bad) { - pkt_discarded = PJ_TRUE; - goto on_return; - } - - /* Ignore if payloadlen is zero */ - if (payloadlen == 0) { - pkt_discarded = PJ_TRUE; - goto on_return; - } - - /* See if source address of RTP packet is different than the - * configured address, and check if we need to tell the - * media transport to switch RTP remote address. - */ - if (param->src_addr) { - pj_bool_t badssrc = (stream->info.has_rem_ssrc && - seq_st.status.flag.badssrc); - - if (pj_sockaddr_cmp(&stream->rem_rtp_addr, param->src_addr) == 0) { - /* We're still receiving from rem_rtp_addr. */ - stream->rtp_src_cnt = 0; - stream->rem_rtp_flag = badssrc? 2: 1; - } else { - stream->rtp_src_cnt++; - - if (stream->rtp_src_cnt < PJMEDIA_RTP_NAT_PROBATION_CNT) { - if (stream->rem_rtp_flag == 1 || - (stream->rem_rtp_flag == 2 && badssrc)) - { - /* Only discard if: - * - we have ever received packet with good ssrc from - * remote address (rem_rtp_addr), or - * - we have ever received packet with bad ssrc from - * remote address and this packet also has bad ssrc. - */ - pkt_discarded = PJ_TRUE; - goto on_return; - } - if (stream->info.has_rem_ssrc && !seq_st.status.flag.badssrc - && stream->rem_rtp_flag != 1) - { - /* Immediately switch if we receive packet with the - * correct ssrc AND we never receive packets with - * good ssrc from rem_rtp_addr. - */ - param->rem_switch = PJ_TRUE; - } - } else { - /* Switch. We no longer receive packets from rem_rtp_addr. */ - param->rem_switch = PJ_TRUE; - } - - if (param->rem_switch) { - /* Set remote RTP address to source address */ - pj_sockaddr_cp(&stream->rem_rtp_addr, param->src_addr); - - /* Reset counter and flag */ - stream->rtp_src_cnt = 0; - stream->rem_rtp_flag = badssrc? 2: 1; - - /* Update RTCP peer ssrc */ - stream->rtcp.peer_ssrc = pj_ntohl(hdr->ssrc); - } - } - } - - pj_grp_lock_acquire( stream->grp_lock ); + pj_grp_lock_acquire( c_strm->grp_lock ); /* Quickly see if there may be a full picture in the jitter buffer, and * decode them if so. More thorough check will be done in decode_frame(). @@ -951,7 +336,7 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) */ pj_bool_t can_decode = PJ_FALSE; - if (pjmedia_jbuf_is_full(stream->jb)) { + if (pjmedia_jbuf_is_full(c_strm->jb)) { can_decode = PJ_TRUE; } else if (stream->dec_frame.size == 0) { @@ -982,32 +367,32 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) * when RTP session is restarted. */ if (seq_st.status.flag.restart) { - status = pjmedia_jbuf_reset(stream->jb); + status = pjmedia_jbuf_reset(c_strm->jb); PJ_LOG(4,(channel->port.info.name.ptr, "Jitter buffer reset")); } else { /* Just put the payload into jitter buffer */ - pjmedia_jbuf_put_frame3(stream->jb, payload, payloadlen, 0, + pjmedia_jbuf_put_frame3(c_strm->jb, payload, payloadlen, 0, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), NULL); #if TRACE_JB - trace_jb_put(stream, hdr, payloadlen, count); + trace_jb_put(c_strm, hdr, payloadlen, 1 /*count*/); #endif } - pj_grp_lock_release( stream->grp_lock ); + pj_grp_lock_release( c_strm->grp_lock ); /* Check if we need to send RTCP-FB generic NACK */ - if (stream->send_rtcp_fb_nack && seq_st.diff > 1 && + if (c_strm->send_rtcp_fb_nack && seq_st.diff > 1 && pj_ntohs(hdr->seq) >= seq_st.diff) { int i; - pj_bzero(&stream->rtcp_fb_nack, sizeof(stream->rtcp_fb_nack)); - stream->rtcp_fb_nack.pid = pj_ntohs(hdr->seq) - seq_st.diff + 1; + pj_bzero(&c_strm->rtcp_fb_nack, sizeof(c_strm->rtcp_fb_nack)); + c_strm->rtcp_fb_nack.pid = pj_ntohs(hdr->seq) - seq_st.diff + 1; for (i = 0; i < (seq_st.diff - 1); ++i) { - stream->rtcp_fb_nack.blp <<= 1; - stream->rtcp_fb_nack.blp |= 1; + c_strm->rtcp_fb_nack.blp <<= 1; + c_strm->rtcp_fb_nack.blp |= 1; } - stream->pending_rtcp_fb_nack = 1; + c_strm->pending_rtcp_fb_nack = 1; } /* Check if now is the time to transmit RTCP SR/RR report. @@ -1015,75 +400,28 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) * if the encoder is paused, * because otherwise check_tx_rtcp() will be handled by put_frame() */ - if (stream->dir == PJMEDIA_DIR_DECODING || stream->enc->paused) { + if (c_strm->dir == PJMEDIA_DIR_DECODING || c_strm->enc->paused) { check_tx_rtcp(stream); } if (status != 0) { LOGERR_((channel->port.info.name.ptr, status, "Jitter buffer put() error")); - pkt_discarded = PJ_TRUE; + *pkt_discarded = PJ_TRUE; goto on_return; } on_return: - /* Update RTCP session */ - if (stream->rtcp.peer_ssrc == 0) - stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc; - - pjmedia_rtcp_rx_rtp2(&stream->rtcp, pj_ntohs(hdr->seq), - pj_ntohl(hdr->ts), payloadlen, pkt_discarded); - - /* Send RTCP RR and SDES after we receive some RTP packets */ - if (stream->rtcp.received >= 10 && !stream->initial_rr) { - status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, - PJ_FALSE, PJ_FALSE, PJ_FALSE); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->name.ptr, status, - "Error sending initial RTCP RR")); - } else { - stream->initial_rr = PJ_TRUE; - } - } - - pj_grp_lock_dec_ref(stream->grp_lock); + return status; } -/* - * This callback is called by stream transport on receipt of packets - * in the RTCP socket. - */ -static void on_rx_rtcp( void *data, - void *pkt, - pj_ssize_t bytes_read) -{ - pjmedia_vid_stream *stream = (pjmedia_vid_stream*) data; - pj_status_t status; - - /* Check for errors */ - if (bytes_read < 0) { - status = (pj_status_t)-bytes_read; - if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { - return; - } - LOGERR_((stream->cname.ptr, status, "Unable to receive RTCP packet")); - if (status == PJ_ESOCKETSTOP) { - /* Publish receive error event. */ - publish_tp_event(PJMEDIA_EVENT_MEDIA_TP_ERR, status, PJ_FALSE, - PJMEDIA_DIR_DECODING, stream); - } - return; - } - - pjmedia_rtcp_rx_rtcp(&stream->rtcp, pkt, bytes_read); -} - static pj_status_t put_frame(pjmedia_port *port, pjmedia_frame *frame) { pjmedia_vid_stream *stream = (pjmedia_vid_stream*) port->port_data.pdata; - pjmedia_vid_channel *channel = stream->enc; + pjmedia_stream_common *c_strm = &stream->base; + pjmedia_vid_channel *channel = c_strm->enc; pj_status_t status = 0; pjmedia_frame frame_out; unsigned rtp_ts_len; @@ -1101,7 +439,7 @@ static pj_status_t put_frame(pjmedia_port *port, /* If the interval since last sending packet is greater than * PJMEDIA_STREAM_KA_INTERVAL, send keep-alive packet. */ - if (stream->use_ka) + if (c_strm->use_ka) { pj_uint32_t dtx_duration, ka_interval; pj_time_val tm_now, tmp; @@ -1109,20 +447,20 @@ static pj_status_t put_frame(pjmedia_port *port, pj_gettimeofday(&tm_now); tmp = tm_now; - PJ_TIME_VAL_SUB(tmp, stream->last_frm_ts_sent); + PJ_TIME_VAL_SUB(tmp, c_strm->last_frm_ts_sent); dtx_duration = PJ_TIME_VAL_MSEC(tmp); - if (stream->start_ka_count) { - ka_interval = stream->start_ka_interval; + if (c_strm->start_ka_count) { + ka_interval = c_strm->start_ka_interval; } else { - ka_interval = stream->ka_interval * 1000; + ka_interval = c_strm->ka_interval * 1000; } if (dtx_duration > ka_interval) { - send_keep_alive_packet(stream); - stream->last_frm_ts_sent = tm_now; + send_keep_alive_packet(c_strm); + c_strm->last_frm_ts_sent = tm_now; - if (stream->start_ka_count) - stream->start_ka_count--; + if (c_strm->start_ka_count) + c_strm->start_ka_count--; } } #endif @@ -1136,7 +474,7 @@ static pj_status_t put_frame(pjmedia_port *port, NULL, NULL); /* Update RTCP stats with last RTP timestamp. */ - stream->rtcp.stat.rtp_tx_last_ts = + c_strm->rtcp.stat.rtp_tx_last_ts = pj_ntohl(channel->rtp.out_hdr.ts); return PJ_SUCCESS; } @@ -1225,25 +563,25 @@ static pj_status_t put_frame(pjmedia_port *port, /* When the payload length is zero, we should not send anything, * but proceed the rest normally. */ - if (frame_out.size != 0 && stream->transport) { + if (frame_out.size != 0 && c_strm->transport) { /* Copy RTP header to the beginning of packet */ pj_memcpy(channel->buf, rtphdr, sizeof(pjmedia_rtp_hdr)); /* Send the RTP packet to the transport. */ - status = pjmedia_transport_send_rtp(stream->transport, + status = pjmedia_transport_send_rtp(c_strm->transport, (char*)channel->buf, frame_out.size + sizeof(pjmedia_rtp_hdr)); if (status != PJ_SUCCESS) { - if (stream->rtp_tx_err_cnt++ == 0) { + if (c_strm->rtp_tx_err_cnt++ == 0) { LOGERR_((channel->port.info.name.ptr, status, "Error sending RTP")); } - if (stream->rtp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) { - stream->rtp_tx_err_cnt = 0; + if (c_strm->rtp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) { + c_strm->rtp_tx_err_cnt = 0; } } - pjmedia_rtcp_tx_rtp(&stream->rtcp, (unsigned)frame_out.size); + pjmedia_rtcp_tx_rtp(&c_strm->rtcp, (unsigned)frame_out.size); total_sent += frame_out.size; pkt_cnt++; } @@ -1300,7 +638,7 @@ static pj_status_t put_frame(pjmedia_port *port, pj_get_timestamp(&end_time); total_sleep = pj_elapsed_msec(&initial_time, &end_time); - PJ_LOG(5, (stream->name.ptr, "total pkt=%d size=%d sleep=%d", + PJ_LOG(5, (c_strm->name.ptr, "total pkt=%d size=%d sleep=%d", pkt_cnt, total_sent, total_sleep)); if (stream->tx_start.u64 == 0) @@ -1316,7 +654,7 @@ static pj_status_t put_frame(pjmedia_port *port, * We only do this when stream direction is not "decoding only", because * when it is, check_tx_rtcp() will be handled by get_frame(). */ - if (stream->dir != PJMEDIA_DIR_DECODING && stream->transport) { + if (c_strm->dir != PJMEDIA_DIR_DECODING && c_strm->transport) { check_tx_rtcp(stream); } @@ -1327,15 +665,15 @@ static pj_status_t put_frame(pjmedia_port *port, /* Update stat */ if (pkt_cnt) { - stream->rtcp.stat.rtp_tx_last_ts = - pj_ntohl(stream->enc->rtp.out_hdr.ts); - stream->rtcp.stat.rtp_tx_last_seq = - pj_ntohs(stream->enc->rtp.out_hdr.seq); + c_strm->rtcp.stat.rtp_tx_last_ts = + pj_ntohl(c_strm->enc->rtp.out_hdr.ts); + c_strm->rtcp.stat.rtp_tx_last_seq = + pj_ntohs(c_strm->enc->rtp.out_hdr.seq); } #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /* Update time of last sending packet. */ - pj_gettimeofday(&stream->last_frm_ts_sent); + pj_gettimeofday(&c_strm->last_frm_ts_sent); #endif return PJ_SUCCESS; @@ -1345,7 +683,8 @@ static pj_status_t put_frame(pjmedia_port *port, static pj_status_t decode_frame(pjmedia_vid_stream *stream, pjmedia_frame *frame) { - pjmedia_vid_channel *channel = stream->dec; + pjmedia_stream_common *c_strm = &stream->base; + pjmedia_vid_channel *channel = c_strm->dec; pj_uint32_t last_ts = 0, frm_ts = 0; pj_bool_t last_ts_inited = PJ_FALSE; int frm_first_seq = 0, frm_last_seq = 0; @@ -1364,12 +703,12 @@ static pj_status_t decode_frame(pjmedia_vid_stream *stream, int seq; /* Peek frame from jitter buffer. */ - pjmedia_jbuf_peek_frame(stream->jb, cnt, NULL, NULL, + pjmedia_jbuf_peek_frame(c_strm->jb, cnt, NULL, NULL, &ptype, NULL, &ts, &seq); if (ptype == PJMEDIA_JB_NORMAL_FRAME) { if (stream->last_dec_ts == ts) { /* Remove any late packet (the frame has been decoded) */ - pjmedia_jbuf_remove_frame(stream->jb, 1); + pjmedia_jbuf_remove_frame(c_strm->jb, 1); continue; } @@ -1408,7 +747,7 @@ static pj_status_t decode_frame(pjmedia_vid_stream *stream, */ for (; frm_pkt_cnt > 1; --frm_pkt_cnt) { char ptype; - pjmedia_jbuf_peek_frame(stream->jb, frm_pkt_cnt, NULL, NULL, &ptype, + pjmedia_jbuf_peek_frame(c_strm->jb, frm_pkt_cnt, NULL, NULL, &ptype, NULL, NULL, NULL); if (ptype == PJMEDIA_JB_NORMAL_FRAME) break; @@ -1419,7 +758,7 @@ static pj_status_t decode_frame(pjmedia_vid_stream *stream, PJ_LOG(1,(channel->port.info.name.ptr, "Discarding %u frames because array is full!", frm_pkt_cnt - stream->rx_frame_cnt)); - pjmedia_jbuf_remove_frame(stream->jb, + pjmedia_jbuf_remove_frame(c_strm->jb, frm_pkt_cnt - stream->rx_frame_cnt); frm_pkt_cnt = stream->rx_frame_cnt; } @@ -1435,7 +774,7 @@ static pj_status_t decode_frame(pjmedia_vid_stream *stream, /* We use jbuf_peek_frame() as it will returns the pointer of * the payload (no buffer and memcpy needed), just as we need. */ - pjmedia_jbuf_peek_frame(stream->jb, i, + pjmedia_jbuf_peek_frame(c_strm->jb, i, (const void**)&stream->rx_frames[i].buf, &stream->rx_frames[i].size, &ptype, NULL, NULL, &frm_last_seq); @@ -1460,7 +799,7 @@ static pj_status_t decode_frame(pjmedia_vid_stream *stream, frame->size = 0; } - pjmedia_jbuf_remove_frame(stream->jb, frm_pkt_cnt); + pjmedia_jbuf_remove_frame(c_strm->jb, frm_pkt_cnt); } /* Learn remote frame rate after successful decoding */ @@ -1503,7 +842,7 @@ static pj_status_t decode_frame(pjmedia_vid_stream *stream, /* Update the decoding delay */ { pjmedia_jb_state jb_state; - pjmedia_jbuf_get_state(stream->jb, &jb_state); + pjmedia_jbuf_get_state(c_strm->jb, &jb_state); stream->dec_delay_cnt = ((PJMEDIA_VID_STREAM_DECODE_MIN_DELAY_MSEC * @@ -1554,7 +893,8 @@ static pj_status_t get_frame(pjmedia_port *port, pjmedia_frame *frame) { pjmedia_vid_stream *stream = (pjmedia_vid_stream*) port->port_data.pdata; - pjmedia_vid_channel *channel = stream->dec; + pjmedia_stream_common *c_strm = &stream->base; + pjmedia_vid_channel *channel = c_strm->dec; /* Return no frame is channel is paused */ if (channel->paused) { @@ -1577,7 +917,7 @@ static pj_status_t get_frame(pjmedia_port *port, if (fmt_chg_data->dir == PJMEDIA_DIR_DECODING) { pjmedia_format_copy(&stream->info.codec_param->dec_fmt, &fmt_chg_data->new_fmt); - pjmedia_format_copy(&stream->dec->port.info.fmt, + pjmedia_format_copy(&c_strm->dec->port.info.fmt, &fmt_chg_data->new_fmt); /* Override the framerate to be 1.5x higher in the event @@ -1588,12 +928,12 @@ static pj_status_t get_frame(pjmedia_port *port, } else { pjmedia_format_copy(&stream->info.codec_param->enc_fmt, &fmt_chg_data->new_fmt); - pjmedia_format_copy(&stream->enc->port.info.fmt, + pjmedia_format_copy(&c_strm->enc->port.info.fmt, &fmt_chg_data->new_fmt); } dump_port_info(fmt_chg_data->dir==PJMEDIA_DIR_DECODING ? - stream->dec : stream->enc, + c_strm->dec : c_strm->enc, "changed"); pjmedia_event_publish(NULL, port, &stream->fmt_event, @@ -1608,7 +948,7 @@ static pj_status_t get_frame(pjmedia_port *port, stream->miss_keyframe_event.type = PJMEDIA_EVENT_NONE; } - pj_grp_lock_acquire( stream->grp_lock ); + pj_grp_lock_acquire( c_strm->grp_lock ); if (stream->dec_frame.size == 0) { /* Don't have frame in buffer, try to decode one */ @@ -1618,7 +958,7 @@ static pj_status_t get_frame(pjmedia_port *port, } } else { if (frame->size < stream->dec_frame.size) { - PJ_LOG(4,(stream->dec->port.info.name.ptr, + PJ_LOG(4,(c_strm->dec->port.info.name.ptr, "Error: not enough buffer for decoded frame " "(supplied=%d, required=%d)", (int)frame->size, (int)stream->dec_frame.size)); @@ -1634,7 +974,7 @@ static pj_status_t get_frame(pjmedia_port *port, stream->dec_frame.size = 0; } - pj_grp_lock_release( stream->grp_lock ); + pj_grp_lock_release( c_strm->grp_lock ); return PJ_SUCCESS; } @@ -1650,6 +990,7 @@ static pj_status_t create_channel( pj_pool_t *pool, pjmedia_vid_channel **p_channel) { enum { M = 32 }; + pjmedia_stream_common *c_strm = &stream->base; pjmedia_vid_channel *channel; pj_status_t status; unsigned min_out_pkt_size; @@ -1679,19 +1020,19 @@ static pj_status_t create_channel( pj_pool_t *pool, pi = &channel->port.info; /* Init channel info. */ - channel->stream = stream; + channel->stream = c_strm; channel->dir = dir; channel->paused = 1; channel->pt = pt; /* Allocate buffer for outgoing packet. */ if (dir == PJMEDIA_DIR_ENCODING) { - channel->buf_size = sizeof(pjmedia_rtp_hdr) + stream->frame_size; + channel->buf_size = sizeof(pjmedia_rtp_hdr) + c_strm->frame_size; /* It should big enough to hold (minimally) RTCP SR with an SDES. */ min_out_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) + sizeof(pjmedia_rtcp_common) + - (4 + (unsigned)stream->cname.slen) + + (4 + (unsigned)c_strm->cname.slen) + 32; if (channel->buf_size < min_out_pkt_size) @@ -1728,7 +1069,7 @@ static pj_status_t create_channel( pj_pool_t *pool, channel->port.port_data.pdata = stream; /* Use stream group lock */ - channel->port.grp_lock = stream->grp_lock; + channel->port.grp_lock = c_strm->grp_lock; PJ_LOG(5, (name.ptr, "%s channel created %dx%d %s%s%.*s %d/%d(~%d)fps", @@ -1761,6 +1102,8 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( enum { M = 32 }; pj_pool_t *own_pool = NULL; pjmedia_vid_stream *stream; + pjmedia_stream_common *c_strm; + pj_str_t name; unsigned jb_init, jb_max, jb_min_pre, jb_max_pre; int frm_ptime, chunks_per_frm; pjmedia_video_format_detail *vfd_enc, *vfd_dec; @@ -1779,15 +1122,21 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( /* Allocate stream */ stream = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_stream); PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM); - stream->own_pool = own_pool; + c_strm = &stream->base; + c_strm->own_pool = own_pool; + + /* Init stream/port name */ + name.ptr = (char*) pj_pool_alloc(pool, M); + name.slen = pj_ansi_snprintf(name.ptr, M, "vstrm%p", stream); + c_strm->port.info.name = name; /* Get codec manager */ stream->codec_mgr = pjmedia_vid_codec_mgr_instance(); PJ_ASSERT_RETURN(stream->codec_mgr, PJMEDIA_CODEC_EFAILED); /* Init stream/port name */ - stream->name.ptr = (char*) pj_pool_alloc(pool, M); - stream->name.slen = pj_ansi_snprintf(stream->name.ptr, M, + c_strm->name.ptr = (char*) pj_pool_alloc(pool, M); + c_strm->name.slen = pj_ansi_snprintf(c_strm->name.ptr, M, "vstrm%p", stream); /* Create and initialize codec: */ @@ -1825,44 +1174,44 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( &info->codec_param->dec_fmt, PJ_TRUE); /* Init stream: */ - stream->endpt = endpt; - stream->dir = info->dir; - stream->user_data = user_data; - stream->rtcp_interval = PJMEDIA_RTCP_INTERVAL + pj_rand()%1000 - 500; - stream->rtcp_sdes_bye_disabled = info->rtcp_sdes_bye_disabled; + c_strm->endpt = endpt; + c_strm->dir = info->dir; + c_strm->user_data = user_data; + c_strm->rtcp_interval = PJMEDIA_RTCP_INTERVAL + pj_rand()%1000 - 500; + c_strm->rtcp_sdes_bye_disabled = info->rtcp_sdes_bye_disabled; - stream->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME; + c_strm->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME; #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 - stream->use_ka = info->use_ka; - stream->ka_interval = info->ka_cfg.ka_interval; - stream->start_ka_count = info->ka_cfg.start_count; - stream->start_ka_interval = info->ka_cfg.start_interval; + c_strm->use_ka = info->use_ka; + c_strm->ka_interval = info->ka_cfg.ka_interval; + c_strm->start_ka_count = info->ka_cfg.start_count; + c_strm->start_ka_interval = info->ka_cfg.start_interval; #endif stream->num_keyframe = info->sk_cfg.count; - stream->cname = info->cname; - if (stream->cname.slen == 0) { + c_strm->cname = info->cname; + if (c_strm->cname.slen == 0) { /* Build random RTCP CNAME. CNAME has user@host format */ - stream->cname.ptr = p = (char*) pj_pool_alloc(pool, 20); + c_strm->cname.ptr = p = (char*) pj_pool_alloc(pool, 20); pj_create_random_string(p, 5); p += 5; *p++ = '@'; *p++ = 'p'; *p++ = 'j'; pj_create_random_string(p, 6); p += 6; *p++ = '.'; *p++ = 'o'; *p++ = 'r'; *p++ = 'g'; - stream->cname.slen = p - stream->cname.ptr; + c_strm->cname.slen = p - c_strm->cname.ptr; } /* Create group lock */ status = pj_grp_lock_create_w_handler(pool, NULL, stream, &on_destroy, - &stream->grp_lock); + &c_strm->grp_lock); if (status != PJ_SUCCESS) goto err_cleanup; /* Add ref count of group lock */ - status = pj_grp_lock_add_ref(stream->grp_lock); + status = pj_grp_lock_add_ref(c_strm->grp_lock); if (status != PJ_SUCCESS) goto err_cleanup; @@ -1879,24 +1228,24 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( stream->codec); /* Estimate the maximum frame size */ - stream->frame_size = vfd_enc->size.w * vfd_enc->size.h * 4; + c_strm->frame_size = vfd_enc->size.w * vfd_enc->size.h * 4; #if 0 - stream->frame_size = vfd_enc->max_bps/8 * vfd_enc->fps.denum / + c_strm->frame_size = vfd_enc->max_bps/8 * vfd_enc->fps.denum / vfd_enc->fps.num; /* As the maximum frame_size is not represented directly by maximum bps * (which includes intra and predicted frames), let's increase the * frame size value for safety. */ - stream->frame_size <<= 4; + c_strm->frame_size <<= 4; #endif /* Validate the frame size */ - if (stream->frame_size == 0 || - stream->frame_size > PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE) + if (c_strm->frame_size == 0 || + c_strm->frame_size > PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE) { - stream->frame_size = PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE; + c_strm->frame_size = PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE; } /* Get frame length in timestamp unit */ @@ -1929,13 +1278,13 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( /* Create decoder channel */ status = create_channel( pool, stream, PJMEDIA_DIR_DECODING, - info->rx_pt, info, &stream->dec); + info->rx_pt, info, &c_strm->dec); if (status != PJ_SUCCESS) goto err_cleanup; /* Create encoder channel */ status = create_channel( pool, stream, PJMEDIA_DIR_ENCODING, - info->tx_pt, info, &stream->enc); + info->tx_pt, info, &c_strm->enc); if (status != PJ_SUCCESS) goto err_cleanup; @@ -1945,7 +1294,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( /* Init jitter buffer parameters: */ frm_ptime = 1000 * vfd_dec->fps.denum / vfd_dec->fps.num; - chunks_per_frm = stream->frame_size / PJMEDIA_MAX_MRU; + chunks_per_frm = c_strm->frame_size / PJMEDIA_MAX_MRU; if (chunks_per_frm < MIN_CHUNKS_PER_FRM) chunks_per_frm = MIN_CHUNKS_PER_FRM; @@ -1995,58 +1344,58 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( sizeof(stream->rx_frames[0])); /* Create jitter buffer */ - status = pjmedia_jbuf_create(pool, &stream->dec->port.info.name, + status = pjmedia_jbuf_create(pool, &c_strm->dec->port.info.name, PJMEDIA_MAX_MRU, 1000 * vfd_enc->fps.denum / vfd_enc->fps.num, - jb_max, &stream->jb); + jb_max, &c_strm->jb); if (status != PJ_SUCCESS) goto err_cleanup; /* Set up jitter buffer */ - pjmedia_jbuf_set_adaptive(stream->jb, jb_init, jb_min_pre, jb_max_pre); - pjmedia_jbuf_set_discard(stream->jb, PJMEDIA_JB_DISCARD_NONE); + pjmedia_jbuf_set_adaptive(c_strm->jb, jb_init, jb_min_pre, jb_max_pre); + pjmedia_jbuf_set_discard(c_strm->jb, PJMEDIA_JB_DISCARD_NONE); /* Init RTCP session: */ { pjmedia_rtcp_session_setting rtcp_setting; pjmedia_rtcp_session_setting_default(&rtcp_setting); - rtcp_setting.name = stream->name.ptr; + rtcp_setting.name = c_strm->name.ptr; rtcp_setting.ssrc = info->ssrc; - rtcp_setting.rtp_ts_base = pj_ntohl(stream->enc->rtp.out_hdr.ts); + rtcp_setting.rtp_ts_base = pj_ntohl(c_strm->enc->rtp.out_hdr.ts); rtcp_setting.clock_rate = info->codec_info.clock_rate; rtcp_setting.samples_per_frame = 1; - pjmedia_rtcp_init2(&stream->rtcp, &rtcp_setting); + pjmedia_rtcp_init2(&c_strm->rtcp, &rtcp_setting); if (info->rtp_seq_ts_set) { - stream->rtcp.stat.rtp_tx_last_seq = info->rtp_seq; - stream->rtcp.stat.rtp_tx_last_ts = info->rtp_ts; + c_strm->rtcp.stat.rtp_tx_last_seq = info->rtp_seq; + c_strm->rtcp.stat.rtp_tx_last_ts = info->rtp_ts; } /* Subscribe to RTCP events */ pjmedia_event_subscribe(NULL, &stream_event_cb, stream, - &stream->rtcp); + &c_strm->rtcp); } /* Allocate outgoing RTCP buffer, should be enough to hold SR/RR, SDES, * BYE, Feedback, and XR. */ - stream->out_rtcp_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) + + c_strm->out_rtcp_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) + sizeof(pjmedia_rtcp_common) + - (4 + (unsigned)stream->cname.slen) + + (4 + (unsigned)c_strm->cname.slen) + 32 + 32; - if (stream->out_rtcp_pkt_size > PJMEDIA_MAX_MTU) - stream->out_rtcp_pkt_size = PJMEDIA_MAX_MTU; + if (c_strm->out_rtcp_pkt_size > PJMEDIA_MAX_MTU) + c_strm->out_rtcp_pkt_size = PJMEDIA_MAX_MTU; - stream->out_rtcp_pkt = pj_pool_alloc(pool, stream->out_rtcp_pkt_size); + c_strm->out_rtcp_pkt = pj_pool_alloc(pool, c_strm->out_rtcp_pkt_size); pj_bzero(&att_param, sizeof(att_param)); att_param.stream = stream; att_param.media_type = PJMEDIA_TYPE_VIDEO; att_param.user_data = stream; pj_sockaddr_cp(&att_param.rem_addr, &info->rem_addr); - pj_sockaddr_cp(&stream->rem_rtp_addr, &info->rem_addr); + pj_sockaddr_cp(&c_strm->rem_rtp_addr, &info->rem_addr); if (info->rtcp_mux) { pj_sockaddr_cp(&att_param.rem_rtcp, &info->rem_addr); } else if (pj_sockaddr_has_addr(&info->rem_rtcp)) { @@ -2061,21 +1410,21 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( if (status != PJ_SUCCESS) goto err_cleanup; - stream->transport = tp; + c_strm->transport = tp; /* Also add ref the transport group lock */ - if (stream->transport->grp_lock) - pj_grp_lock_add_ref(stream->transport->grp_lock); + if (c_strm->transport->grp_lock) + pj_grp_lock_add_ref(c_strm->transport->grp_lock); /* Send RTCP SDES */ - if (!stream->rtcp_sdes_bye_disabled) { + if (!c_strm->rtcp_sdes_bye_disabled) { pjmedia_vid_stream_send_rtcp_sdes(stream); } #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /* NAT hole punching by sending KA packet via RTP transport. */ - if (stream->use_ka) - send_keep_alive_packet(stream); + if (c_strm->use_ka) + send_keep_alive_packet(c_strm); #endif #if TRACE_JB @@ -2085,32 +1434,35 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( pj_ansi_snprintf(trace_name, sizeof(trace_name), TRACE_JB_PATH_PREFIX "%s.csv", - channel->port.info.name.ptr); + c_strm->port.info.name.ptr); status = pj_file_open(pool, trace_name, PJ_O_RDWR, - &stream->trace_jb_fd); + &c_strm->trace_jb_fd); if (status != PJ_SUCCESS) { - stream->trace_jb_fd = TRACE_JB_INVALID_FD; + c_strm->trace_jb_fd = TRACE_JB_INVALID_FD; PJ_PERROR(3,(THIS_FILE, status, "Failed creating RTP trace file '%s'", trace_name)); } else { - stream->trace_jb_buf = (char*)pj_pool_alloc(pool, PJ_LOG_MAX_SIZE); + c_strm->trace_jb_buf = (char*)pj_pool_alloc(pool, PJ_LOG_MAX_SIZE); /* Print column header */ - len = pj_ansi_snprintf(stream->trace_jb_buf, PJ_LOG_MAX_SIZE, + len = pj_ansi_snprintf(c_strm->trace_jb_buf, PJ_LOG_MAX_SIZE, "Time, Operation, Size, Frame Count, " "Frame type, RTP Seq, RTP TS, RTP M, " "JB size, JB burst level, JB prefetch\n"); if (len < 1 || len >= PJ_LOG_MAX_SIZE) len = PJ_LOG_MAX_SIZE - 1; - pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); - pj_file_flush(stream->trace_jb_fd); + pj_file_write(c_strm->trace_jb_fd, c_strm->trace_jb_buf, &len); + pj_file_flush(c_strm->trace_jb_fd); } + + PJ_UNUSED_ARG(trace_jb_get); } #endif /* Save the stream info */ pj_memcpy(&stream->info, info, sizeof(*info)); + c_strm->si = (pjmedia_stream_info_common *)&stream->info; stream->info.codec_param = pjmedia_vid_codec_param_clone( pool, info->codec_param); pjmedia_rtcp_fb_info_dup(pool, &stream->info.loc_rtcp_fb, @@ -2126,18 +1478,18 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( for (i = 0; i < rfi->cap_count; ++i) { if (rfi->caps[i].type == PJMEDIA_RTCP_FB_NACK) { if (rfi->caps[i].param.slen == 0) { - stream->send_rtcp_fb_nack = PJ_TRUE; - PJ_LOG(5,(stream->name.ptr, "Send RTCP-FB generic NACK")); + c_strm->send_rtcp_fb_nack = PJ_TRUE; + PJ_LOG(5,(c_strm->name.ptr, "Send RTCP-FB generic NACK")); } else if (pj_stricmp2(&rfi->caps[i].param, "pli")==0) { stream->send_rtcp_fb_pli = PJ_TRUE; - PJ_LOG(5,(stream->name.ptr, "Send RTCP-FB PLI")); + PJ_LOG(5,(c_strm->name.ptr, "Send RTCP-FB PLI")); } } } } /* Check if we should process incoming RTCP-FB */ - stream->rtcp_fb_nack_cap_idx = -1; + c_strm->rtcp_fb_nack_cap_idx = -1; stream->rtcp_fb_pli_cap_idx = -1; if (stream->info.loc_rtcp_fb.cap_count) { pjmedia_rtcp_fb_info *lfi = &stream->info.loc_rtcp_fb; @@ -2146,12 +1498,12 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( for (i = 0; i < lfi->cap_count; ++i) { if (lfi->caps[i].type == PJMEDIA_RTCP_FB_NACK) { if (lfi->caps[i].param.slen == 0) { - stream->rtcp_fb_nack_cap_idx = i; - PJ_LOG(5,(stream->name.ptr, + c_strm->rtcp_fb_nack_cap_idx = i; + PJ_LOG(5,(c_strm->name.ptr, "Receive RTCP-FB generic NACK")); } else if (pj_stricmp2(&lfi->caps[i].param, "pli")==0) { stream->rtcp_fb_pli_cap_idx = i; - PJ_LOG(5,(stream->name.ptr, "Receive RTCP-FB PLI")); + PJ_LOG(5,(c_strm->name.ptr, "Receive RTCP-FB PLI")); } } } @@ -2160,7 +1512,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( /* Success! */ *p_stream = stream; - PJ_LOG(5,(THIS_FILE, "Video stream %s created", stream->name.ptr)); + PJ_LOG(5,(THIS_FILE, "Video stream %s created", c_strm->name.ptr)); return PJ_SUCCESS; @@ -2175,22 +1527,24 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( */ PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy( pjmedia_vid_stream *stream ) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); - PJ_LOG(4,(THIS_FILE, "Destroy request on %s..", stream->name.ptr)); + PJ_LOG(4,(THIS_FILE, "Destroy request on %s..", c_strm->name.ptr)); /* Stop the streaming */ - if (stream->enc) - stream->enc->port.put_frame = NULL; - if (stream->dec) - stream->dec->port.get_frame = NULL; + if (c_strm->enc) + c_strm->enc->port.put_frame = NULL; + if (c_strm->dec) + c_strm->dec->port.get_frame = NULL; #if TRACE_RC { unsigned total_time; total_time = pj_elapsed_msec(&stream->tx_start, &stream->tx_end); - PJ_LOG(5, (stream->name.ptr, + PJ_LOG(5, (c_strm->name.ptr, "RC stat: pkt_cnt=%.2f/image, sleep=%.2fms/s, fps=%.2f", stream->rc_total_pkt*1.0/stream->rc_total_img, stream->rc_total_sleep*1000.0/total_time, @@ -2203,27 +1557,28 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy( pjmedia_vid_stream *stream ) pjmedia_event_unsubscribe(NULL, &stream_event_cb, stream, stream->codec); } - pjmedia_event_unsubscribe(NULL, &stream_event_cb, stream, &stream->rtcp); + pjmedia_event_unsubscribe(NULL, &stream_event_cb, stream, &c_strm->rtcp); /* Send RTCP BYE (also SDES) */ - if (stream->transport && !stream->rtcp_sdes_bye_disabled) { - send_rtcp(stream, PJ_TRUE, PJ_TRUE, PJ_FALSE, PJ_FALSE); + if (c_strm->transport && !c_strm->rtcp_sdes_bye_disabled) { + send_rtcp(c_strm, PJ_TRUE, PJ_TRUE, PJ_FALSE, PJ_FALSE, PJ_FALSE, + PJ_FALSE); } /* Detach from transport * MUST NOT hold stream mutex while detaching from transport, as * it may cause deadlock. See ticket #460 for the details. */ - if (stream->transport) { - pjmedia_transport_detach(stream->transport, stream); - //stream->transport = NULL; + if (c_strm->transport) { + pjmedia_transport_detach(c_strm->transport, stream); + //c_strm->transport = NULL; } /* This function may be called when stream is partly initialized, * i.e: group lock may not be created yet. */ - if (stream->grp_lock) { - return pj_grp_lock_dec_ref(stream->grp_lock); + if (c_strm->grp_lock) { + return pj_grp_lock_dec_ref(c_strm->grp_lock); } else { on_destroy(stream); } @@ -2235,16 +1590,9 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy( pjmedia_vid_stream *stream ) /* * Destroy stream. */ -static void on_destroy( void *arg ) +static void on_stream_destroy( void *arg ) { pjmedia_vid_stream *stream = (pjmedia_vid_stream*)arg; - pj_assert(stream); - - PJ_LOG(4,(THIS_FILE, "Destroying %s..", stream->name.ptr)); - - /* Release ref to transport */ - if (stream->transport && stream->transport->grp_lock) - pj_grp_lock_dec_ref(stream->transport->grp_lock); /* Free codec. */ if (stream->codec) { @@ -2252,24 +1600,6 @@ static void on_destroy( void *arg ) pjmedia_vid_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec); stream->codec = NULL; } - - /* Free mutex */ - stream->grp_lock = NULL; - - /* Destroy jitter buffer */ - if (stream->jb) { - pjmedia_jbuf_destroy(stream->jb); - stream->jb = NULL; - } - -#if TRACE_JB - if (TRACE_JB_OPENED(stream)) { - pj_file_close(stream->trace_jb_fd); - stream->trace_jb_fd = TRACE_JB_INVALID_FD; - } -#endif - - pj_pool_safe_release(&stream->own_pool); } @@ -2280,13 +1610,15 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_port(pjmedia_vid_stream *stream, pjmedia_dir dir, pjmedia_port **p_port ) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(dir==PJMEDIA_DIR_ENCODING || dir==PJMEDIA_DIR_DECODING, PJ_EINVAL); if (dir == PJMEDIA_DIR_ENCODING) - *p_port = &stream->enc->port; + *p_port = &c_strm->enc->port; else - *p_port = &stream->dec->port; + *p_port = &c_strm->dec->port; return PJ_SUCCESS; } @@ -2298,7 +1630,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_port(pjmedia_vid_stream *stream, PJ_DEF(pjmedia_transport*) pjmedia_vid_stream_get_transport( pjmedia_vid_stream *st) { - return st->transport; + return st->base.transport; } @@ -2311,7 +1643,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_stat( { PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL); - pj_memcpy(stat, &stream->rtcp.stat, sizeof(pjmedia_rtcp_stat)); + pj_memcpy(stat, &stream->base.rtcp.stat, sizeof(pjmedia_rtcp_stat)); return PJ_SUCCESS; } @@ -2323,7 +1655,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_reset_stat(pjmedia_vid_stream *stream) { PJ_ASSERT_RETURN(stream, PJ_EINVAL); - pjmedia_rtcp_init_stat(&stream->rtcp.stat); + pjmedia_rtcp_init_stat(&stream->base.rtcp.stat); return PJ_SUCCESS; } @@ -2337,7 +1669,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_stat_jbuf( pjmedia_jb_state *state) { PJ_ASSERT_RETURN(stream && state, PJ_EINVAL); - return pjmedia_jbuf_get_state(stream->jb, state); + return pjmedia_jbuf_get_state(stream->base.jb, state); } @@ -2359,23 +1691,24 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_info( */ PJ_DEF(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); - PJ_ASSERT_RETURN(stream && stream->enc && stream->dec, PJ_EINVALIDOP); + PJ_ASSERT_RETURN(stream && c_strm->enc && c_strm->dec, PJ_EINVALIDOP); - if (stream->enc && (stream->dir & PJMEDIA_DIR_ENCODING)) { - stream->enc->paused = 0; - //pjmedia_snd_stream_start(stream->enc->snd_stream); - PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream started")); + if (c_strm->enc && (c_strm->dir & PJMEDIA_DIR_ENCODING)) { + c_strm->enc->paused = 0; + //pjmedia_snd_stream_start(c_strm->enc->snd_stream); + PJ_LOG(4,(c_strm->enc->port.info.name.ptr, "Encoder stream started")); } else { - PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream paused")); + PJ_LOG(4,(c_strm->enc->port.info.name.ptr, "Encoder stream paused")); } - if (stream->dec && (stream->dir & PJMEDIA_DIR_DECODING)) { - stream->dec->paused = 0; - //pjmedia_snd_stream_start(stream->dec->snd_stream); - PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream started")); + if (c_strm->dec && (c_strm->dir & PJMEDIA_DIR_DECODING)) { + c_strm->dec->paused = 0; + //pjmedia_snd_stream_start(c_strm->dec->snd_stream); + PJ_LOG(4,(c_strm->dec->port.info.name.ptr, "Decoder stream started")); } else { - PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream paused")); + PJ_LOG(4,(c_strm->dec->port.info.name.ptr, "Decoder stream paused")); } return PJ_SUCCESS; @@ -2401,16 +1734,17 @@ pjmedia_vid_stream_modify_codec_param(pjmedia_vid_stream *stream, PJ_DEF(pj_bool_t) pjmedia_vid_stream_is_running(pjmedia_vid_stream *stream, pjmedia_dir dir) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); pj_bool_t is_running = PJ_TRUE; PJ_ASSERT_RETURN(stream, PJ_FALSE); if (dir & PJMEDIA_DIR_ENCODING) { - is_running &= (stream->enc && !stream->enc->paused); + is_running &= (c_strm->enc && !c_strm->enc->paused); } if (dir & PJMEDIA_DIR_DECODING) { - is_running &= (stream->dec && !stream->dec->paused); + is_running &= (c_strm->dec && !c_strm->dec->paused); } return is_running; @@ -2422,22 +1756,24 @@ PJ_DEF(pj_bool_t) pjmedia_vid_stream_is_running(pjmedia_vid_stream *stream, PJ_DEF(pj_status_t) pjmedia_vid_stream_pause(pjmedia_vid_stream *stream, pjmedia_dir dir) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream, PJ_EINVAL); - if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) { - stream->enc->paused = 1; - PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream paused")); + if ((dir & PJMEDIA_DIR_ENCODING) && c_strm->enc) { + c_strm->enc->paused = 1; + PJ_LOG(4,(c_strm->enc->port.info.name.ptr, "Encoder stream paused")); } - if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) { - stream->dec->paused = 1; + if ((dir & PJMEDIA_DIR_DECODING) && c_strm->dec) { + c_strm->dec->paused = 1; /* Also reset jitter buffer */ - pj_grp_lock_acquire( stream->grp_lock ); - pjmedia_jbuf_reset(stream->jb); - pj_grp_lock_release( stream->grp_lock ); + pj_grp_lock_acquire( c_strm->grp_lock ); + pjmedia_jbuf_reset(c_strm->jb); + pj_grp_lock_release( c_strm->grp_lock ); - PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream paused")); + PJ_LOG(4,(c_strm->dec->port.info.name.ptr, "Decoder stream paused")); } return PJ_SUCCESS; @@ -2450,18 +1786,20 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_pause(pjmedia_vid_stream *stream, PJ_DEF(pj_status_t) pjmedia_vid_stream_resume(pjmedia_vid_stream *stream, pjmedia_dir dir) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream, PJ_EINVAL); - if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) { - stream->enc->paused = 0; + if ((dir & PJMEDIA_DIR_ENCODING) && c_strm->enc) { + c_strm->enc->paused = 0; stream->force_keyframe = PJ_TRUE; - PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream resumed")); + PJ_LOG(4,(c_strm->enc->port.info.name.ptr, "Encoder stream resumed")); } - if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) { - stream->dec->paused = 0; + if ((dir & PJMEDIA_DIR_DECODING) && c_strm->dec) { + c_strm->dec->paused = 0; stream->last_dec_seq = 0; - PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream resumed")); + PJ_LOG(4,(c_strm->dec->port.info.name.ptr, "Decoder stream resumed")); } return PJ_SUCCESS; @@ -2500,9 +1838,8 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_send_keyframe( PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_sdes( pjmedia_vid_stream *stream) { - PJ_ASSERT_RETURN(stream, PJ_EINVAL); - - return send_rtcp(stream, PJ_TRUE, PJ_FALSE, PJ_FALSE, PJ_FALSE); + return pjmedia_stream_common_send_rtcp_sdes( + (pjmedia_stream_common *) stream); } @@ -2512,13 +1849,8 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_sdes( PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_bye( pjmedia_vid_stream *stream) { - PJ_ASSERT_RETURN(stream, PJ_EINVAL); - - if (stream->enc && stream->transport) { - return send_rtcp(stream, PJ_TRUE, PJ_TRUE, PJ_FALSE, PJ_FALSE); - } - - return PJ_SUCCESS; + return pjmedia_stream_common_send_rtcp_bye( + (pjmedia_stream_common *) stream); } @@ -2528,10 +1860,13 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_bye( PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_pli( pjmedia_vid_stream *stream) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream, PJ_EINVAL); - if (stream->transport) { - return send_rtcp(stream, PJ_FALSE, PJ_FALSE, PJ_FALSE, PJ_TRUE); + if (c_strm->transport) { + return send_rtcp(c_strm, PJ_FALSE, PJ_FALSE, PJ_FALSE, PJ_FALSE, + PJ_FALSE, PJ_TRUE); } return PJ_SUCCESS; @@ -2568,10 +1903,8 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_rtp_session_info(pjmedia_vid_stream *stream, pjmedia_stream_rtp_sess_info *session_info) { - session_info->rx_rtp = &stream->dec->rtp; - session_info->tx_rtp = &stream->enc->rtp; - session_info->rtcp = &stream->rtcp; - return PJ_SUCCESS; + return pjmedia_stream_common_get_rtp_session_info( + (pjmedia_stream_common *)stream, session_info); } diff --git a/pjmedia/src/pjmedia/vid_stream_info.c b/pjmedia/src/pjmedia/vid_stream_info.c index 3e9e60dab4..d24bd64ba3 100644 --- a/pjmedia/src/pjmedia/vid_stream_info.c +++ b/pjmedia/src/pjmedia/vid_stream_info.c @@ -23,10 +23,6 @@ #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) -static const pj_str_t ID_IN = { "IN", 2 }; -static const pj_str_t ID_IP4 = { "IP4", 3}; -static const pj_str_t ID_IP6 = { "IP6", 3}; -//static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 }; static const pj_str_t ID_RTPMAP = { "rtpmap", 6 }; /* @@ -180,237 +176,31 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_info_from_sdp( const pjmedia_sdp_session *remote, unsigned stream_idx) { - const pj_str_t STR_INACTIVE = { "inactive", 8 }; - const pj_str_t STR_SENDONLY = { "sendonly", 8 }; - const pj_str_t STR_RECVONLY = { "recvonly", 8 }; - - const pjmedia_sdp_attr *attr; + pjmedia_stream_info_common *csi = (pjmedia_stream_info_common *)si; const pjmedia_sdp_media *local_m; const pjmedia_sdp_media *rem_m; - const pjmedia_sdp_conn *local_conn; - const pjmedia_sdp_conn *rem_conn; - int rem_af, local_af; - unsigned i; + pj_bool_t active; pj_status_t status; - PJ_UNUSED_ARG(endpt); + PJ_ASSERT_RETURN(si, PJ_EINVAL); + pj_bzero(si, sizeof(*si)); - /* Validate arguments: */ - PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); - PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); - PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); + status = pjmedia_stream_info_common_from_sdp(csi, pool, endpt, local, + remote, stream_idx, &active); + if (status != PJ_SUCCESS || !active) + return status; /* Keep SDP shortcuts */ local_m = local->media[stream_idx]; rem_m = remote->media[stream_idx]; - local_conn = local_m->conn ? local_m->conn : local->conn; - if (local_conn == NULL) - return PJMEDIA_SDP_EMISSINGCONN; - - rem_conn = rem_m->conn ? rem_m->conn : remote->conn; - if (rem_conn == NULL) - return PJMEDIA_SDP_EMISSINGCONN; - - /* Media type must be video */ + /* Media type must be audio */ if (pjmedia_get_type(&local_m->desc.media) != PJMEDIA_TYPE_VIDEO) return PJMEDIA_EINVALIMEDIATYPE; - - /* Reset: */ - - pj_bzero(si, sizeof(*si)); - - /* Media type: */ - si->type = PJMEDIA_TYPE_VIDEO; - - /* Transport protocol */ - - /* At this point, transport type must be compatible, - * the transport instance will do more validation later. - */ - status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, - &local_m->desc.transport); - if (status != PJ_SUCCESS) - return PJMEDIA_SDPNEG_EINVANSTP; - - /* Get the transport protocol */ - si->proto = pjmedia_sdp_transport_get_proto(&local_m->desc.transport); - - /* Return success if transport protocol is not RTP/AVP compatible */ - if (!PJMEDIA_TP_PROTO_HAS_FLAG(si->proto, PJMEDIA_TP_PROTO_RTP_AVP)) - return PJ_SUCCESS; - - - /* Check address family in remote SDP */ - rem_af = pj_AF_UNSPEC(); - if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) { - if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) { - rem_af = pj_AF_INET(); - } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) { - rem_af = pj_AF_INET6(); - } - } - - if (rem_af==pj_AF_UNSPEC()) { - /* Unsupported address family */ - return PJ_EAFNOTSUP; - } - - /* Set remote address: */ - status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, - rem_m->desc.port); - if (status != PJ_SUCCESS) { - /* Invalid IP address. */ - return PJMEDIA_EINVALIDIP; - } - - /* Check address family of local info */ - local_af = pj_AF_UNSPEC(); - if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) { - if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) { - local_af = pj_AF_INET(); - } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) { - local_af = pj_AF_INET6(); - } - } - - if (local_af==pj_AF_UNSPEC()) { - /* Unsupported address family */ - return PJ_SUCCESS; - } - - /* Set remote address: */ - status = pj_sockaddr_init(local_af, &si->local_addr, &local_conn->addr, - local_m->desc.port); - if (status != PJ_SUCCESS) { - /* Invalid IP address. */ - return PJMEDIA_EINVALIDIP; - } - - /* Local and remote address family must match, except when ICE is used - * by both sides (see also ticket #1952). - */ - if (local_af != rem_af) { - const pj_str_t STR_ICE_CAND = { "candidate", 9 }; - if (pjmedia_sdp_media_find_attr(rem_m, &STR_ICE_CAND, NULL)==NULL || - pjmedia_sdp_media_find_attr(local_m, &STR_ICE_CAND, NULL)==NULL) - { - return PJ_EAFNOTSUP; - } - } - - /* Media direction: */ - - if (local_m->desc.port == 0 || - pj_sockaddr_has_addr(&si->local_addr)==PJ_FALSE || - pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE || - pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) - { - /* Inactive stream. */ - - si->dir = PJMEDIA_DIR_NONE; - - } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { - - /* Send only stream. */ - - si->dir = PJMEDIA_DIR_ENCODING; - - } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { - - /* Recv only stream. */ - - si->dir = PJMEDIA_DIR_DECODING; - - } else { - - /* Send and receive stream. */ - - si->dir = PJMEDIA_DIR_ENCODING_DECODING; - - } - - /* No need to do anything else if stream is rejected */ - if (local_m->desc.port == 0) { - return PJ_SUCCESS; - } - - /* Check if "rtcp-mux" is present in the SDP. */ - attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, - "rtcp-mux", NULL); - if (attr) - si->rtcp_mux = PJ_TRUE; - - /* If "rtcp" attribute is present in the SDP, set the RTCP address - * from that attribute. Otherwise, calculate from RTP address. - */ - attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, - "rtcp", NULL); - if (attr) { - pjmedia_sdp_rtcp_attr rtcp; - status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); - if (status == PJ_SUCCESS) { - if (rtcp.addr.slen) { - pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr, - (pj_uint16_t)rtcp.port); - } else { - pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, - (pj_uint16_t)rtcp.port); - pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp), - pj_sockaddr_get_addr(&si->rem_addr), - pj_sockaddr_get_addr_len(&si->rem_addr)); - } - } - } - - if (!pj_sockaddr_has_addr(&si->rem_rtcp)) { - int rtcp_port; - - pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr)); - rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1; - pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port); - } - - /* Check if "ssrc" attribute is present in the SDP. */ - for (i = 0; i < rem_m->attr_count; i++) { - if (pj_strcmp2(&rem_m->attr[i]->name, "ssrc") == 0) { - pjmedia_sdp_ssrc_attr ssrc; - - status = pjmedia_sdp_attr_get_ssrc( - (const pjmedia_sdp_attr *)rem_m->attr[i], &ssrc); - if (status == PJ_SUCCESS) { - si->has_rem_ssrc = PJ_TRUE; - si->rem_ssrc = ssrc.ssrc; - if (ssrc.cname.slen > 0) { - pj_strdup(pool, &si->rem_cname, &ssrc.cname); - break; - } - } - } - } - /* Get codec info and param */ status = get_video_codec_info_param(si, pool, NULL, local_m, rem_m); - /* Leave SSRC to random. */ - si->ssrc = pj_rand(); - - /* Set default jitter buffer parameter. */ - si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; - - /* Get local RTCP-FB info */ - status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, local, stream_idx, - si->rx_pt, &si->loc_rtcp_fb); - if (status != PJ_SUCCESS) - return status; - - /* Get remote RTCP-FB info */ - status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, remote, stream_idx, - si->tx_pt, &si->rem_rtcp_fb); - if (status != PJ_SUCCESS) - return status; - return status; } diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index bfcd5d0b24..ba1819e8a4 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -3913,6 +3913,333 @@ static pj_bool_t is_media_changed(const pjsua_call *call, #endif /* PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO */ +/* Apply media update. */ +static pj_status_t apply_med_update(pjsua_call_media *call_med, + const pjmedia_sdp_session *local_sdp, + const pjmedia_sdp_session *remote_sdp, + pj_bool_t *need_renego_sdp) +{ + const char *STR_SENDRECV = "sendrecv"; + const char *STR_SENDONLY = "sendonly"; + const char *STR_RECVONLY = "recvonly"; + const char *STR_INACTIVE = "inactive"; + + pjsua_call *call = call_med->call; + pjsua_acc *acc = &pjsua_var.acc[call->acc_id]; + pjsua_call_id call_id = call->index; + unsigned mi = call_med->idx; + pj_bool_t media_changed = PJ_FALSE; + pj_pool_t *tmp_pool = call->inv->pool_prov; + pj_status_t status = PJ_SUCCESS; + + pjmedia_stream_info asi; +#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) + pjmedia_vid_stream_info vsi; +#endif + pjmedia_stream_info_common *si; + pjsua_stream_info stream_info; + pj_str_t *enc_name = NULL; + + if (call_med->type == PJMEDIA_TYPE_AUDIO) { + si = (pjmedia_stream_info_common *)&asi; + status = pjmedia_stream_info_from_sdp( + &asi, tmp_pool, pjsua_var.med_endpt, + local_sdp, remote_sdp, mi); + stream_info.info.aud = asi; + enc_name = &asi.fmt.encoding_name; + +#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) + } else if (call_med->type == PJMEDIA_TYPE_VIDEO) { + si = (pjmedia_stream_info_common *)&vsi; + status = pjmedia_vid_stream_info_from_sdp( + &vsi, tmp_pool, pjsua_var.med_endpt, + local_sdp, remote_sdp, mi); + stream_info.info.vid = vsi; + enc_name = &vsi.codec_info.encoding_name; +#endif + } + + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, + "pjmedia_stream_info_from_sdp() failed " + "for call_id %d media %d", + call_id, mi)); + return status; + } + +#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) + /* Enable/disable RTCP XR based on account setting. */ + si->rtcp_xr_enabled = acc->cfg.enable_rtcp_xr; +#endif + + /* Check if remote wants RTP and RTCP multiplexing, + * but we don't enable it. + */ + if (si->rtcp_mux && !call_med->enable_rtcp_mux) { + si->rtcp_mux = PJ_FALSE; + } + + if (call_med->type == PJMEDIA_TYPE_AUDIO) { + /* Codec parameter of stream info (si->param) can be NULL if + * the stream is rejected or disabled. + */ + /* Override ptime, if this option is specified. */ + if (pjsua_var.media_cfg.ptime != 0 && asi.param) { + asi.param->setting.frm_per_pkt = (pj_uint8_t) + (pjsua_var.media_cfg.ptime / asi.param->info.frm_ptime); + if (asi.param->setting.frm_per_pkt == 0) + asi.param->setting.frm_per_pkt = 1; + } + + /* Disable VAD, if this option is specified. */ + if (pjsua_var.media_cfg.no_vad && asi.param) { + asi.param->setting.vad = 0; + } + + if (call->audio_idx==-1 && status==PJ_SUCCESS && + si->dir != PJMEDIA_DIR_NONE) + { + call->audio_idx = mi; + } + } + + if (!pjmedia_sdp_neg_was_answer_remote(call->inv->neg) && + si->dir != PJMEDIA_DIR_NONE) + { + pjmedia_dir dir = si->dir; + + if (call->opt.flag & PJSUA_CALL_SET_MEDIA_DIR) { + call_med->def_dir = call->opt.media_dir[mi]; + PJ_LOG(4,(THIS_FILE, "Call %d: setting audio media " + "direction #%d to %d.", + call_id, mi, call_med->def_dir)); + } + + /* If the default direction specifies we do not wish + * encoding/decoding, clear that direction. + */ + if ((call_med->def_dir & PJMEDIA_DIR_ENCODING) == 0) { + dir &= ~PJMEDIA_DIR_ENCODING; + } + if ((call_med->def_dir & PJMEDIA_DIR_DECODING) == 0) { + dir &= ~PJMEDIA_DIR_DECODING; + } + + if (dir != si->dir) { + const char *str_attr = NULL; + pjmedia_sdp_attr *attr; + pjmedia_sdp_media *m; + + if (!*need_renego_sdp) { + pjmedia_sdp_session *local_sdp_renego; + local_sdp_renego = + pjmedia_sdp_session_clone(tmp_pool, local_sdp); + local_sdp = local_sdp_renego; + *need_renego_sdp = PJ_TRUE; + } + + si->dir = dir; + m = local_sdp->media[mi]; + + /* Remove existing directions attributes */ + pjmedia_sdp_media_remove_all_attr(m, STR_SENDRECV); + pjmedia_sdp_media_remove_all_attr(m, STR_SENDONLY); + pjmedia_sdp_media_remove_all_attr(m, STR_RECVONLY); + + if (si->dir == PJMEDIA_DIR_ENCODING_DECODING) { + str_attr = STR_SENDRECV; + } else if (si->dir == PJMEDIA_DIR_ENCODING) { + str_attr = STR_SENDONLY; + } else if (si->dir == PJMEDIA_DIR_DECODING) { + str_attr = STR_RECVONLY; + } else { + str_attr = STR_INACTIVE; + } + attr = pjmedia_sdp_attr_create(tmp_pool, str_attr, NULL); + pjmedia_sdp_media_add_attr(m, attr); + } + } + + stream_info.type = call_med->type; + +#if PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /* Check if we need to reset or maintain SRTP ROC */ + check_srtp_roc(call, mi, &stream_info, local_sdp, remote_sdp); +#endif +#endif + + /* Check if this media is changed */ + if (pjsua_var.media_cfg.no_smart_media_update || + is_media_changed(call, mi, &stream_info)) + { + media_changed = PJ_TRUE; + /* Stop the media */ + stop_media_stream(call, mi); + } else { + PJ_LOG(4,(THIS_FILE, "Call %d: stream #%d (%s) unchanged.", + call_id, mi, pjmedia_type_name(call_med->type))); + } + + /* Check if no media is active */ + if (local_sdp->media[mi]->desc.port == 0) { + + /* Update call media state and direction */ + call_med->state = PJSUA_CALL_MEDIA_NONE; + call_med->dir = PJMEDIA_DIR_NONE; + + } else if (call_med->tp) { + pjmedia_transport_info tp_info; + pjmedia_srtp_info *srtp_info; + + /* Call media direction */ + call_med->dir = si->dir; + + /* Call media state */ + if (call->local_hold || + ((call_med->dir & PJMEDIA_DIR_DECODING) == 0 && + (call_med->def_dir & PJMEDIA_DIR_DECODING) == 0)) + { + /* Local hold: Either the user holds the call, or sets + * the media direction that requests the remote party to + * stop sending media (i.e. sendonly or inactive). + */ + call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD; + } else if ((call_med->dir & PJMEDIA_DIR_ENCODING) == 0 && + (call_med->def_dir & PJMEDIA_DIR_DECODING) != 0) + { + /* Remote hold: Remote doesn't want us to send media + * (recvonly or inactive) and we don't set media dir that + * locally holds the media. + */ + call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD; + } else { + call_med->state = PJSUA_CALL_MEDIA_ACTIVE; + } + + if (call->inv->following_fork) { + unsigned options = (call_med->enable_rtcp_mux? + PJMEDIA_TPMED_RTCP_MUX: 0); + /* Normally media transport will automatically restart + * itself (if needed, based on info from the SDP) in + * pjmedia_transport_media_start(), however in "following + * forked media" case (see #1644), we need to explicitly + * restart it as it cannot detect fork scenario from + * the SDP only. + */ + status = pjmedia_transport_media_stop(call_med->tp); + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, + "pjmedia_transport_media_stop() failed " + "for call_id %d media %d", + call_id, mi)); + return status; + } + status = pjmedia_transport_media_create(call_med->tp, + tmp_pool, + options, NULL, mi); + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, + "pjmedia_transport_media_create() failed " + "for call_id %d media %d", + call_id, mi)); + return status; + } + } + + /* Start/restart media transport based on info in SDP */ + status = pjmedia_transport_media_start(call_med->tp, + tmp_pool, local_sdp, + remote_sdp, mi); + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, + "pjmedia_transport_media_start() failed " + "for call_id %d media %d", + call_id, mi)); + return status; + } + + pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING); + + /* Get remote SRTP usage policy */ + pjmedia_transport_info_init(&tp_info); + pjmedia_transport_get_info(call_med->tp, &tp_info); + srtp_info = (pjmedia_srtp_info*) + pjmedia_transport_info_get_spc_info( + &tp_info, PJMEDIA_TRANSPORT_TYPE_SRTP); + if (srtp_info) { + call_med->rem_srtp_use = srtp_info->peer_use; + } + + /* Update audio channel */ + if (media_changed) { + if (call_med->type == PJMEDIA_TYPE_AUDIO) { + status = pjsua_aud_channel_update(call_med, + call->inv->pool, &asi, + local_sdp, remote_sdp); + } else if (call_med->type == PJMEDIA_TYPE_VIDEO) { +#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) + status = pjsua_vid_channel_update(call_med, + call->inv->pool, &vsi, + local_sdp, remote_sdp); +#endif + } + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, + "pjsua_aud_channel_update() failed " + "for call_id %d media %d", + call_id, mi)); + return status; + } + + if (pjmedia_transport_info_get_spc_info( + &tp_info, PJMEDIA_TRANSPORT_TYPE_LOOP)) + { + pjmedia_transport_loop_disable_rx( + call_med->tp, call_med->strm.a.stream, + !acc->cfg.enable_loopback); + } + } + } + + /* Print info. */ + if (status == PJ_SUCCESS) { + char info[80]; + int info_len = 0; + int len; + const char *dir; + + switch (si->dir) { + case PJMEDIA_DIR_NONE: + dir = "inactive"; + break; + case PJMEDIA_DIR_ENCODING: + dir = "sendonly"; + break; + case PJMEDIA_DIR_DECODING: + dir = "recvonly"; + break; + case PJMEDIA_DIR_ENCODING_DECODING: + dir = "sendrecv"; + break; + default: + dir = "unknown"; + break; + } + len = pj_ansi_snprintf( info+info_len, sizeof(info)-info_len, + ", stream #%d: %.*s (%s)", mi, + (enc_name? (int)enc_name->slen: 0), + (enc_name? enc_name->ptr: info), + dir); + if (len > 0) + info_len += len; + PJ_LOG(4,(THIS_FILE,"%s updated%s", + pjmedia_type_name(call_med->type), info)); + } + return status; +} + + pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, const pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *remote_sdp) @@ -4025,12 +4352,7 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, /* Process each media stream */ for (mi=0; mi < call->med_cnt; ++mi) { - const char *STR_SENDRECV = "sendrecv"; - const char *STR_SENDONLY = "sendonly"; - const char *STR_RECVONLY = "recvonly"; - const char *STR_INACTIVE = "inactive"; pjsua_call_media *call_med = &call->media[mi]; - pj_bool_t media_changed = PJ_FALSE; if (mi >= local_sdp->media_count || mi >= remote_sdp->media_count) @@ -4060,490 +4382,15 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, /* Apply media update action */ if (call_med->type==PJMEDIA_TYPE_AUDIO) { - pjmedia_stream_info the_si, *si = &the_si; - pjsua_stream_info stream_info; - - status = pjmedia_stream_info_from_sdp( - si, tmp_pool, pjsua_var.med_endpt, - local_sdp, remote_sdp, mi); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjmedia_stream_info_from_sdp() failed " - "for call_id %d media %d", - call_id, mi)); + status = apply_med_update(call_med, local_sdp, remote_sdp, &need_renego_sdp); + if (status != PJ_SUCCESS) goto on_check_med_status; - } - -#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) - /* Enable/disable RTCP XR based on account setting. */ - si->rtcp_xr_enabled = acc->cfg.enable_rtcp_xr; -#endif - - /* Check if remote wants RTP and RTCP multiplexing, - * but we don't enable it. - */ - if (si->rtcp_mux && !call_med->enable_rtcp_mux) { - si->rtcp_mux = PJ_FALSE; - } - - /* Codec parameter of stream info (si->param) can be NULL if - * the stream is rejected or disabled. - */ - /* Override ptime, if this option is specified. */ - if (pjsua_var.media_cfg.ptime != 0 && si->param) { - si->param->setting.frm_per_pkt = (pj_uint8_t) - (pjsua_var.media_cfg.ptime / si->param->info.frm_ptime); - if (si->param->setting.frm_per_pkt == 0) - si->param->setting.frm_per_pkt = 1; - } - - /* Disable VAD, if this option is specified. */ - if (pjsua_var.media_cfg.no_vad && si->param) { - si->param->setting.vad = 0; - } - - if (!pjmedia_sdp_neg_was_answer_remote(call->inv->neg) && - si->dir != PJMEDIA_DIR_NONE) - { - pjmedia_dir dir = si->dir; - - if (call->opt.flag & PJSUA_CALL_SET_MEDIA_DIR) { - call_med->def_dir = call->opt.media_dir[mi]; - PJ_LOG(4,(THIS_FILE, "Call %d: setting audio media " - "direction #%d to %d.", - call_id, mi, call_med->def_dir)); - } - - /* If the default direction specifies we do not wish - * encoding/decoding, clear that direction. - */ - if ((call_med->def_dir & PJMEDIA_DIR_ENCODING) == 0) { - dir &= ~PJMEDIA_DIR_ENCODING; - } - if ((call_med->def_dir & PJMEDIA_DIR_DECODING) == 0) { - dir &= ~PJMEDIA_DIR_DECODING; - } - - if (dir != si->dir) { - const char *str_attr = NULL; - pjmedia_sdp_attr *attr; - pjmedia_sdp_media *m; - - if (!need_renego_sdp) { - pjmedia_sdp_session *local_sdp_renego; - local_sdp_renego = - pjmedia_sdp_session_clone(tmp_pool, local_sdp); - local_sdp = local_sdp_renego; - need_renego_sdp = PJ_TRUE; - } - - si->dir = dir; - m = local_sdp->media[mi]; - - /* Remove existing directions attributes */ - pjmedia_sdp_media_remove_all_attr(m, STR_SENDRECV); - pjmedia_sdp_media_remove_all_attr(m, STR_SENDONLY); - pjmedia_sdp_media_remove_all_attr(m, STR_RECVONLY); - - if (si->dir == PJMEDIA_DIR_ENCODING_DECODING) { - str_attr = STR_SENDRECV; - } else if (si->dir == PJMEDIA_DIR_ENCODING) { - str_attr = STR_SENDONLY; - } else if (si->dir == PJMEDIA_DIR_DECODING) { - str_attr = STR_RECVONLY; - } else { - str_attr = STR_INACTIVE; - } - attr = pjmedia_sdp_attr_create(tmp_pool, str_attr, NULL); - pjmedia_sdp_media_add_attr(m, attr); - } - } - - stream_info.type = PJMEDIA_TYPE_AUDIO; - stream_info.info.aud = the_si; - -#if PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO -#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) - /* Check if we need to reset or maintain SRTP ROC */ - check_srtp_roc(call, mi, &stream_info, local_sdp, remote_sdp); -#endif -#endif - - /* Check if this media is changed */ - if (pjsua_var.media_cfg.no_smart_media_update || - is_media_changed(call, mi, &stream_info)) - { - media_changed = PJ_TRUE; - /* Stop the media */ - stop_media_stream(call, mi); - } else { - PJ_LOG(4,(THIS_FILE, "Call %d: stream #%d (audio) unchanged.", - call_id, mi)); - } - - /* Check if no media is active */ - if (local_sdp->media[mi]->desc.port == 0) { - - /* Update call media state and direction */ - call_med->state = PJSUA_CALL_MEDIA_NONE; - call_med->dir = PJMEDIA_DIR_NONE; - - } else if (call_med->tp) { - pjmedia_transport_info tp_info; - pjmedia_srtp_info *srtp_info; - - /* Call media direction */ - call_med->dir = si->dir; - - /* Call media state */ - if (call->local_hold || - ((call_med->dir & PJMEDIA_DIR_DECODING) == 0 && - (call_med->def_dir & PJMEDIA_DIR_DECODING) == 0)) - { - /* Local hold: Either the user holds the call, or sets - * the media direction that requests the remote party to - * stop sending media (i.e. sendonly or inactive). - */ - call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD; - } else if ((call_med->dir & PJMEDIA_DIR_ENCODING) == 0 && - (call_med->def_dir & PJMEDIA_DIR_DECODING) != 0) - { - /* Remote hold: Remote doesn't want us to send media - * (recvonly or inactive) and we don't set media dir that - * locally holds the media. - */ - call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD; - } else { - call_med->state = PJSUA_CALL_MEDIA_ACTIVE; - } - - if (call->inv->following_fork) { - unsigned options = (call_med->enable_rtcp_mux? - PJMEDIA_TPMED_RTCP_MUX: 0); - /* Normally media transport will automatically restart - * itself (if needed, based on info from the SDP) in - * pjmedia_transport_media_start(), however in "following - * forked media" case (see #1644), we need to explicitly - * restart it as it cannot detect fork scenario from - * the SDP only. - */ - status = pjmedia_transport_media_stop(call_med->tp); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjmedia_transport_media_stop() failed " - "for call_id %d media %d", - call_id, mi)); - goto on_check_med_status; - } - status = pjmedia_transport_media_create(call_med->tp, - tmp_pool, - options, NULL, mi); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjmedia_transport_media_create() failed " - "for call_id %d media %d", - call_id, mi)); - goto on_check_med_status; - } - } - - /* Start/restart media transport based on info in SDP */ - status = pjmedia_transport_media_start(call_med->tp, - tmp_pool, local_sdp, - remote_sdp, mi); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjmedia_transport_media_start() failed " - "for call_id %d media %d", - call_id, mi)); - goto on_check_med_status; - } - - pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING); - - /* Get remote SRTP usage policy */ - pjmedia_transport_info_init(&tp_info); - pjmedia_transport_get_info(call_med->tp, &tp_info); - srtp_info = (pjmedia_srtp_info*) - pjmedia_transport_info_get_spc_info( - &tp_info, PJMEDIA_TRANSPORT_TYPE_SRTP); - if (srtp_info) { - call_med->rem_srtp_use = srtp_info->peer_use; - } - - /* Update audio channel */ - if (media_changed) { - status = pjsua_aud_channel_update(call_med, - call->inv->pool, si, - local_sdp, remote_sdp); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjsua_aud_channel_update() failed " - "for call_id %d media %d", - call_id, mi)); - goto on_check_med_status; - } - - if (pjmedia_transport_info_get_spc_info( - &tp_info, PJMEDIA_TRANSPORT_TYPE_LOOP)) - { - pjmedia_transport_loop_disable_rx( - call_med->tp, call_med->strm.a.stream, - !acc->cfg.enable_loopback); - } - } - } - - /* Print info. */ - if (status == PJ_SUCCESS) { - char info[80]; - int info_len = 0; - int len; - const char *dir; - - switch (si->dir) { - case PJMEDIA_DIR_NONE: - dir = "inactive"; - break; - case PJMEDIA_DIR_ENCODING: - dir = "sendonly"; - break; - case PJMEDIA_DIR_DECODING: - dir = "recvonly"; - break; - case PJMEDIA_DIR_ENCODING_DECODING: - dir = "sendrecv"; - break; - default: - dir = "unknown"; - break; - } - len = pj_ansi_snprintf( info+info_len, sizeof(info)-info_len, - ", stream #%d: %.*s (%s)", mi, - (int)si->fmt.encoding_name.slen, - si->fmt.encoding_name.ptr, - dir); - if (len > 0) - info_len += len; - PJ_LOG(4,(THIS_FILE,"Audio updated%s", info)); - } - - - if (call->audio_idx==-1 && status==PJ_SUCCESS && - si->dir != PJMEDIA_DIR_NONE) - { - call->audio_idx = mi; - } #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) } else if (call_med->type==PJMEDIA_TYPE_VIDEO) { - pjmedia_vid_stream_info the_si, *si = &the_si; - pjsua_stream_info stream_info; - - status = pjmedia_vid_stream_info_from_sdp( - si, tmp_pool, pjsua_var.med_endpt, - local_sdp, remote_sdp, mi); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjmedia_vid_stream_info_from_sdp() failed " - "for call_id %d media %d", - call_id, mi)); + status = apply_med_update(call_med, local_sdp, remote_sdp, &need_renego_sdp); + if (status != PJ_SUCCESS) goto on_check_med_status; - } - - /* Check if remote wants RTP and RTCP multiplexing, - * but we don't enable it. - */ - if (si->rtcp_mux && !call_med->enable_rtcp_mux) { - si->rtcp_mux = PJ_FALSE; - } - - if (!pjmedia_sdp_neg_was_answer_remote(call->inv->neg) && - si->dir != PJMEDIA_DIR_NONE) - { - pjmedia_dir dir = si->dir; - - if (call->opt.flag & PJSUA_CALL_SET_MEDIA_DIR) { - call_med->def_dir = call->opt.media_dir[mi]; - PJ_LOG(4,(THIS_FILE, "Call %d: setting video media " - "direction #%d to %d.", - call_id, mi, call_med->def_dir)); - } - - /* If the default direction specifies we do not wish - * encoding/decoding, clear that direction. - */ - if ((call_med->def_dir & PJMEDIA_DIR_ENCODING) == 0) { - dir &= ~PJMEDIA_DIR_ENCODING; - } - if ((call_med->def_dir & PJMEDIA_DIR_DECODING) == 0) { - dir &= ~PJMEDIA_DIR_DECODING; - } - - if (dir != si->dir) { - const char *str_attr = NULL; - pjmedia_sdp_attr *attr; - pjmedia_sdp_media *m; - - if (!need_renego_sdp) { - pjmedia_sdp_session *local_sdp_renego; - local_sdp_renego = - pjmedia_sdp_session_clone(tmp_pool, local_sdp); - local_sdp = local_sdp_renego; - need_renego_sdp = PJ_TRUE; - } - - si->dir = dir; - m = local_sdp->media[mi]; - - /* Remove existing directions attributes */ - pjmedia_sdp_media_remove_all_attr(m, STR_SENDRECV); - pjmedia_sdp_media_remove_all_attr(m, STR_SENDONLY); - pjmedia_sdp_media_remove_all_attr(m, STR_RECVONLY); - - if (si->dir == PJMEDIA_DIR_ENCODING_DECODING) { - str_attr = STR_SENDRECV; - } else if (si->dir == PJMEDIA_DIR_ENCODING) { - str_attr = STR_SENDONLY; - } else if (si->dir == PJMEDIA_DIR_DECODING) { - str_attr = STR_RECVONLY; - } else { - str_attr = STR_INACTIVE; - } - attr = pjmedia_sdp_attr_create(tmp_pool, str_attr, NULL); - pjmedia_sdp_media_add_attr(m, attr); - } - } - - stream_info.type = PJMEDIA_TYPE_VIDEO; - stream_info.info.vid = the_si; - -#if PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO -#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) - /* Check if we need to reset or maintain SRTP ROC */ - check_srtp_roc(call, mi, &stream_info, local_sdp, remote_sdp); -#endif -#endif - - /* Check if this media is changed */ - if (is_media_changed(call, mi, &stream_info)) { - media_changed = PJ_TRUE; - /* Stop the media */ - stop_media_stream(call, mi); - } else { - PJ_LOG(4,(THIS_FILE, "Call %d: stream #%d (video) unchanged.", - call_id, mi)); - } - - /* Check if no media is active */ - if (local_sdp->media[mi]->desc.port == 0) { - - /* Update call media state and direction */ - call_med->state = PJSUA_CALL_MEDIA_NONE; - call_med->dir = PJMEDIA_DIR_NONE; - - } else if (call_med->tp) { - pjmedia_transport_info tp_info; - pjmedia_srtp_info *srtp_info; - - /* Call media direction */ - call_med->dir = si->dir; - - /* Call media state */ - if (call->local_hold || - ((call_med->dir & PJMEDIA_DIR_DECODING) == 0 && - (call_med->def_dir & PJMEDIA_DIR_DECODING) == 0)) - { - /* Local hold: Either the user holds the call, or sets - * the media direction that requests the remote party to - * stop sending media (i.e. sendonly or inactive). - */ - call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD; - } else if ((call_med->dir & PJMEDIA_DIR_ENCODING) == 0 && - (call_med->def_dir & PJMEDIA_DIR_DECODING) != 0) - { - /* Remote hold: Remote doesn't want us to send media - * (recvonly or inactive) and we don't set media dir that - * locally holds the media. - */ - call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD; - } else { - call_med->state = PJSUA_CALL_MEDIA_ACTIVE; - } - - /* Start/restart media transport */ - status = pjmedia_transport_media_start(call_med->tp, - tmp_pool, local_sdp, - remote_sdp, mi); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjmedia_transport_media_start() failed " - "for call_id %d media %d", - call_id, mi)); - goto on_check_med_status; - } - - pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING); - - /* Get remote SRTP usage policy */ - pjmedia_transport_info_init(&tp_info); - pjmedia_transport_get_info(call_med->tp, &tp_info); - srtp_info = (pjmedia_srtp_info*) - pjmedia_transport_info_get_spc_info( - &tp_info, PJMEDIA_TRANSPORT_TYPE_SRTP); - if (srtp_info) { - call_med->rem_srtp_use = srtp_info->peer_use; - } - - /* Update video channel */ - if (media_changed) { - status = pjsua_vid_channel_update(call_med, - call->inv->pool, si, - local_sdp, remote_sdp); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjsua_vid_channel_update() failed " - "for call_id %d media %d", - call_id, mi)); - goto on_check_med_status; - } - } - } - - /* Print info. */ - { - char info[80]; - int info_len = 0; - int len; - const char *dir; - - switch (si->dir) { - case PJMEDIA_DIR_NONE: - dir = "inactive"; - break; - case PJMEDIA_DIR_ENCODING: - dir = "sendonly"; - break; - case PJMEDIA_DIR_DECODING: - dir = "recvonly"; - break; - case PJMEDIA_DIR_ENCODING_DECODING: - dir = "sendrecv"; - break; - default: - dir = "unknown"; - break; - } - len = pj_ansi_snprintf( info+info_len, sizeof(info)-info_len, - ", stream #%d: %.*s (%s)", mi, - (int)si->codec_info.encoding_name.slen, - si->codec_info.encoding_name.ptr, - dir); - if (len > 0) - info_len += len; - PJ_LOG(4,(THIS_FILE,"Video updated%s", info)); - } - #endif } else { status = PJMEDIA_EUNSUPMEDIATYPE; From 1e2f12134fcd415739f4625c2be60029a6670615 Mon Sep 17 00:00:00 2001 From: sauwming Date: Thu, 13 Feb 2025 10:30:58 +0800 Subject: [PATCH 198/491] Fixed CI Mac failure (#4304) --- .github/workflows/ci-mac.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index da90e835f8..79108f6025 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -217,6 +217,8 @@ jobs: cirunner/installmac.sh - name: install dependencies run: brew install gnutls swig + - name: config site + run: cd pjlib/include/pj && cp config_site_test.h config_site.h - name: configure run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --with-gnutls=`brew --prefix gnutls` - name: make @@ -257,6 +259,8 @@ jobs: cirunner/installmac.sh - name: install dependencies run: brew install gnutls swig + - name: config site + run: cd pjlib/include/pj && cp config_site_test.h config_site.h - name: configure run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --with-gnutls=`brew --prefix gnutls` - name: make @@ -388,7 +392,7 @@ jobs: - name: build ffmpeg run: cd FFmpeg && $MAKE_FAST && sudo make install - name: config site - run: echo -e "#define PJMEDIA_HAS_VIDEO 1\n" > pjlib/include/pj/config_site.h + run: cd pjlib/include/pj && cp config_site_test.h config_site.h && echo -e "#define PJMEDIA_HAS_VIDEO 1\n" >> config_site.h - name: configure run: CFLAGS="$(pkg-config --cflags openssl) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib" CXXFLAGS="-fPIC" ./configure - name: make @@ -420,7 +424,7 @@ jobs: - name: install dependencies run: brew install openssl libvpx swig - name: config site - run: echo -e "#define PJMEDIA_HAS_VIDEO 1\n#define PJMEDIA_HAS_VID_TOOLBOX_CODEC 1\n" > pjlib/include/pj/config_site.h + run: cd pjlib/include/pj && cp config_site_test.h config_site.h && echo -e "#define PJMEDIA_HAS_VIDEO 1\n#define PJMEDIA_HAS_VID_TOOLBOX_CODEC 1\n" >> config_site.h - name: configure run: CFLAGS="$(pkg-config --cflags openssl) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib" CXXFLAGS="-fPIC" ./configure - name: make From e533af23c19d65496b8a8016a531bcd24899800c Mon Sep 17 00:00:00 2001 From: Riza Sulistyo Date: Thu, 13 Feb 2025 14:20:55 +0700 Subject: [PATCH 199/491] Add the option to check the source address of ClientHello message on DTLS-SRTP (#4261) * Check the source address of ClientHello message on DTLS-SRTP * Change the return status * Add check if source adress is available * Modification based on comments - The check will always be performed when ICE is use - Check the RTP/RTPC address against the candidate when ICE is use * Modification based on comments * Modifications based on comment: - Remove the enum candidate APIs - Move the source checking for ICE - Use the remote candidate which has received STUN request or has completed the connectivity check as a valid source - Don't send respond to STUN request with USE-CANDIDATE when the ICE is completed. * Missed lock release * Modification based on comments * Modification based on comments - store the valid address to avoid checking with all of remote candidate - add comment regarding the check source address in SRTP-DTLS is for non-ICE use * Modification based on comments - Revert the ICE behavior change since it cause the pjnath-test to fail - Settle the valid remote address after the ICE is completed * Modification based on comments - Add delay before settling to a remote address after ICE is completed - When ICE is not use, delay the handshake until remote address is available * Minor modification * Add PJSUA2 API --- pjmedia/include/pjmedia/config.h | 10 ++++ pjmedia/src/pjmedia/transport_srtp_dtls.c | 45 ++++++++++++++++++ pjnath/include/pjnath/config.h | 22 +++++++++ pjnath/include/pjnath/ice_session.h | 24 ++++++++++ pjnath/src/pjnath/ice_session.c | 57 ++++++++++++++++++++++- pjsip/include/pjsua2/account.hpp | 9 ++++ pjsip/src/pjsua2/account.cpp | 5 ++ 7 files changed, 171 insertions(+), 1 deletion(-) diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h index 9656b6f31f..231fd7aaaa 100644 --- a/pjmedia/include/pjmedia/config.h +++ b/pjmedia/include/pjmedia/config.h @@ -1100,6 +1100,16 @@ # define PJMEDIA_SRTP_DTLS_OSSL_CIPHERS "DEFAULT" #endif +/** + * Enabled this to check the source address of ClientHello message coming + * from a valid address. See PJ_ICE_SESS_CHECK_SRC_ADDR when ICE is used. + * + * Default value: 0 + */ +#ifndef PJMEDIA_SRTP_DTLS_CHECK_HELLO_ADDR +# define PJMEDIA_SRTP_DTLS_CHECK_HELLO_ADDR 0 +#endif + /** * Maximum number of SRTP cryptos. diff --git a/pjmedia/src/pjmedia/transport_srtp_dtls.c b/pjmedia/src/pjmedia/transport_srtp_dtls.c index f57ba1bc57..a71ec3f797 100644 --- a/pjmedia/src/pjmedia/transport_srtp_dtls.c +++ b/pjmedia/src/pjmedia/transport_srtp_dtls.c @@ -1361,6 +1361,51 @@ static pj_status_t dtls_on_recv(pjmedia_transport *tp, unsigned idx, (ds->setup == DTLS_SETUP_ACTPASS || ds->setup == DTLS_SETUP_PASSIVE)) { pj_status_t status; + +#if defined(PJMEDIA_SRTP_DTLS_CHECK_HELLO_ADDR) && \ + PJMEDIA_SRTP_DTLS_CHECK_HELLO_ADDR==1 + + if (!ds->use_ice) { + pjmedia_transport_info info; + pj_sockaddr *src_addr; + pj_sockaddr *rem_addr; + + /* Check the source address with the specified remote address from + * the SDP. At this point, if the remote address information + * is not available yet (e.g.: remote SDP has not been received), + * delay the handshake. + * Note: when ICE is used, the source address checking will be + * done in ICE session. + */ + if (!ds->rem_fingerprint.slen) { + PJ_LOG(4, (ds->base.name, "DTLS-SRTP %s delaying the handshake " + "until remote address is ready", + CHANNEL_TO_STRING(idx))); + DTLS_UNLOCK(ds); + return PJ_SUCCESS; + } + pjmedia_transport_get_info(ds->srtp->member_tp, &info); + if (idx == RTP_CHANNEL) { + rem_addr = &ds->rem_addr; + src_addr = &info.src_rtp_name; + } else { + rem_addr = &ds->rem_rtcp; + src_addr = &info.src_rtcp_name; + } + + if (pj_sockaddr_cmp(src_addr, rem_addr) != 0) { + char psrc_addr[PJ_INET6_ADDRSTRLEN] = {0}; + + pj_sockaddr_print(src_addr, psrc_addr, sizeof(psrc_addr), 3); + PJ_LOG(4, (ds->base.name, "DTLS-SRTP %s ignoring %lu bytes, " + "from unrecognized src addr %s", CHANNEL_TO_STRING(idx), + (unsigned long)size, psrc_addr)); + + DTLS_UNLOCK(ds); + return PJ_SUCCESS; + } + } +#endif ds->setup = DTLS_SETUP_PASSIVE; status = ssl_handshake_channel(ds, idx); if (status != PJ_SUCCESS) { diff --git a/pjnath/include/pjnath/config.h b/pjnath/include/pjnath/config.h index e904c3ac48..797ae93685 100644 --- a/pjnath/include/pjnath/config.h +++ b/pjnath/include/pjnath/config.h @@ -394,6 +394,28 @@ # define PJ_ICE_NOMINATED_CHECK_DELAY (4*PJ_STUN_RTO_VALUE) #endif +/** + * Specify whether to check the source address of the incoming messages. + * The source address will be compared to the remote candidate which has + * a completed connectivity check or received a connectivity check. + * + * Default: 1 (yes) + */ +#ifndef PJ_ICE_SESS_CHECK_SRC_ADDR +# define PJ_ICE_SESS_CHECK_SRC_ADDR 1 +#endif + + /** + * If ICE source address check is enabled, any incoming data is allowed + * from all possible remote candidates until ICE is completed. Use this + * configuration to specify the time to wait before setting the remote + * address to a fix address. + * + * Defalut: 1000ms + */ +#ifndef PJ_ICE_SESS_SET_RADDR_DELAY +# define PJ_ICE_SESS_SET_RADDR_DELAY 1000 +#endif /** * Minimum interval value to be used for sending STUN keep-alive on the ICE diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h index e796b2539a..b4296d3692 100644 --- a/pjnath/include/pjnath/ice_session.h +++ b/pjnath/include/pjnath/ice_session.h @@ -201,6 +201,12 @@ typedef struct pj_ice_sess_comp */ pj_stun_session *stun_sess; + /** + * The remote candidate checked address. This is expected address that + * the remote going to use. + */ + pj_sockaddr rcand_check_addr; + } pj_ice_sess_comp; @@ -317,6 +323,13 @@ struct pj_ice_sess_cand */ pj_sockaddr rel_addr; + /** + * Indicate that remote connectivity check has been received or the check + * has been successful for this candidate. It is applicable for + * remote candidate only. + * + */ + pj_bool_t checked; }; @@ -672,6 +685,15 @@ typedef struct pj_ice_sess_options */ pj_ice_sess_trickle trickle; + /** + * Specify whether to check the source address of the incoming messages. + * The source address will be compared to the remote candidate which has + * a completed connectivity check or received a connectivity check. + * + * Default value is PJ_ICE_SESS_CHECK_SRC_ADDR. + */ + pj_bool_t check_src_addr; + } pj_ice_sess_options; @@ -705,6 +727,8 @@ struct pj_ice_sess pj_timer_entry timer; /**< ICE timer. */ pj_timer_entry timer_end_of_cand; /**< End-of-cand timer. */ pj_ice_sess_cb cb; /**< Callback. */ + pj_time_val time_completed; /**< The time when ICE + is completed. */ pj_stun_config stun_cfg; /**< STUN settings. */ diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c index 8afe4d181c..d751c04ffb 100644 --- a/pjnath/src/pjnath/ice_session.c +++ b/pjnath/src/pjnath/ice_session.c @@ -325,6 +325,7 @@ PJ_DEF(void) pj_ice_sess_options_default(pj_ice_sess_options *opt) opt->controlled_agent_want_nom_timeout = ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT; opt->trickle = PJ_ICE_SESS_TRICKLE_DISABLED; + opt->check_src_addr = PJ_ICE_SESS_CHECK_SRC_ADDR; } /* @@ -1370,6 +1371,7 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) if (!ice->is_complete) { ice->is_complete = PJ_TRUE; ice->ice_status = status; + pj_gettimeofday(&ice->time_completed); pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer, TIMER_NONE); @@ -2883,6 +2885,8 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, &ice->clist, check), (check->nominated ? " (nominated)" : " (not nominated)"))); + check->rcand->checked = PJ_TRUE; + /* Get the STUN XOR-MAPPED-ADDRESS attribute. */ xaddr = (pj_stun_xor_mapped_addr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR,0); @@ -3141,7 +3145,6 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, uc_attr = (pj_stun_use_candidate_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USE_CANDIDATE, 0); - /* Get ICE-CONTROLLING or ICE-CONTROLLED */ role_attr = (pj_stun_uint64_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ICE_CONTROLLING, 0); @@ -3447,6 +3450,8 @@ static void handle_incoming_check(pj_ice_sess *ice, */ c->nominated = ((rcheck->use_candidate) || c->nominated); + rcand->checked = PJ_TRUE; + if (c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN || c->state == PJ_ICE_SESS_CHECK_STATE_WAITING) { @@ -3734,6 +3739,56 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, PJ_RACE_ME(5); + if (ice->opt.check_src_addr) { + pj_bool_t check_addr = PJ_TRUE; + pj_sockaddr *raddr = &comp->rcand_check_addr; + + if (!pj_sockaddr_has_addr(raddr)) { + for (i = 0; i < ice->rcand_cnt; ++i) { + if (ice->rcand[i].comp_id == comp_id && + ice->rcand[i].checked && + pj_sockaddr_cmp(src_addr, &ice->rcand[i].addr) == 0) + { + /* Before ICE completed, it is still allowed to receive + * data from other candidates. + */ + if (ice->is_complete) { + pj_time_val now; + pj_uint32_t duration; + + pj_gettimeofday(&now); + PJ_TIME_VAL_SUB(now, ice->time_completed); + duration = PJ_TIME_VAL_MSEC(now); + + if (duration > PJ_ICE_SESS_SET_RADDR_DELAY) { + char psrc_addr[PJ_INET6_ADDRSTRLEN] = {0}; + + if (pj_sockaddr_has_addr(src_addr)) { + pj_sockaddr_print(src_addr, psrc_addr, + sizeof(psrc_addr), 3); + } + pj_sockaddr_cp(raddr, src_addr); + PJ_LOG(4, (ice->obj_name, "Using %s as valid" + " address for component [%d]", + psrc_addr, comp_id)); + } + } + check_addr = PJ_FALSE; + break; + } + } + } + if (check_addr && pj_sockaddr_cmp(src_addr, raddr) != 0) { + char paddr[PJ_INET6_ADDRSTRLEN] = {0}; + + pj_sockaddr_print(src_addr, paddr, sizeof(paddr), 3); + PJ_LOG(4, (ice->obj_name, "Ignoring incoming message for " + "component [%d] because source addr %s unrecognized", + comp_id, paddr)); + return PJ_SUCCESS; + } + } + (*ice->cb.on_rx_data)(ice, comp_id, transport_id, pkt, pkt_size, src_addr, src_addr_len); status = PJ_SUCCESS; diff --git a/pjsip/include/pjsua2/account.hpp b/pjsip/include/pjsua2/account.hpp index 458f5644bd..0eaa2b5f9b 100644 --- a/pjsip/include/pjsua2/account.hpp +++ b/pjsip/include/pjsua2/account.hpp @@ -603,6 +603,15 @@ struct AccountNatConfig : public PersistentObject */ int iceWaitNominationTimeoutMsec; + /** + * Specify whether to check the source address of the incoming messages. + * The source address will be compared to the remote candidate which has + * a completed connectivity check or received a connectivity check. + * + * Default value is PJ_ICE_SESS_CHECK_SRC_ADDR. + */ + unsigned iceCheckSrcAddr; + /** * Disable RTCP component. * diff --git a/pjsip/src/pjsua2/account.cpp b/pjsip/src/pjsua2/account.cpp index d40a200c3d..6af262e6c9 100644 --- a/pjsip/src/pjsua2/account.cpp +++ b/pjsip/src/pjsua2/account.cpp @@ -406,6 +406,7 @@ void AccountNatConfig::readObject(const ContainerNode &node) NODE_READ_BOOL ( this_node, iceAggressiveNomination); NODE_READ_UNSIGNED( this_node, iceNominatedCheckDelayMsec); NODE_READ_INT ( this_node, iceWaitNominationTimeoutMsec); + NODE_READ_INT ( this_node, iceCheckSrcAddr); NODE_READ_BOOL ( this_node, iceNoRtcp); NODE_READ_BOOL ( this_node, iceAlwaysUpdate); NODE_READ_BOOL ( this_node, turnEnabled); @@ -442,6 +443,7 @@ void AccountNatConfig::writeObject(ContainerNode &node) const NODE_WRITE_BOOL ( this_node, iceAggressiveNomination); NODE_WRITE_UNSIGNED( this_node, iceNominatedCheckDelayMsec); NODE_WRITE_INT ( this_node, iceWaitNominationTimeoutMsec); + NODE_WRITE_INT ( this_node, iceCheckSrcAddr); NODE_WRITE_BOOL ( this_node, iceNoRtcp); NODE_WRITE_BOOL ( this_node, iceAlwaysUpdate); NODE_WRITE_BOOL ( this_node, turnEnabled); @@ -671,6 +673,7 @@ void AccountConfig::toPj(pjsua_acc_config &ret) const natConfig.iceNominatedCheckDelayMsec; ret.ice_cfg.ice_opt.controlled_agent_want_nom_timeout = natConfig.iceWaitNominationTimeoutMsec; + ret.ice_cfg.ice_opt.check_src_addr = natConfig.iceCheckSrcAddr; ret.ice_cfg.ice_no_rtcp = natConfig.iceNoRtcp; ret.ice_cfg.ice_always_update = natConfig.iceAlwaysUpdate; @@ -842,6 +845,7 @@ void AccountConfig::fromPj(const pjsua_acc_config &prm, prm.ice_cfg.ice_opt.nominated_check_delay; natConfig.iceWaitNominationTimeoutMsec = prm.ice_cfg.ice_opt.controlled_agent_want_nom_timeout; + natConfig.iceCheckSrcAddr = prm.ice_cfg.ice_opt.check_src_addr; natConfig.iceNoRtcp = PJ2BOOL(prm.ice_cfg.ice_no_rtcp); natConfig.iceAlwaysUpdate = PJ2BOOL(prm.ice_cfg.ice_always_update); } else { @@ -856,6 +860,7 @@ void AccountConfig::fromPj(const pjsua_acc_config &prm, mcfg->ice_opt.nominated_check_delay; natConfig.iceWaitNominationTimeoutMsec = mcfg->ice_opt.controlled_agent_want_nom_timeout; + natConfig.iceCheckSrcAddr = mcfg->ice_opt.check_src_addr; natConfig.iceNoRtcp = PJ2BOOL(mcfg->ice_no_rtcp); natConfig.iceAlwaysUpdate = PJ2BOOL(mcfg->ice_always_update); } From 139203b64bb266b939c3c5b53496edfcf252de66 Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 14 Feb 2025 16:44:15 +0800 Subject: [PATCH 200/491] Fixed Coverity warnings (#4305) --- pjmedia/src/pjmedia/stream.c | 20 ++++++++++---------- pjmedia/src/pjmedia/stream_imp_common.c | 3 ++- pjmedia/src/pjmedia/vid_stream.c | 15 ++++++++------- pjsip/include/pjsua2/account.hpp | 1 + pjsip/src/pjsua-lib/pjsua_media.c | 5 +++++ 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index 28d8bc31c1..5bfc261b33 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -2403,7 +2403,7 @@ static void on_stream_destroy(void *arg) */ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) { - pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; pj_status_t status; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); @@ -2530,7 +2530,7 @@ PJ_DEF(pjmedia_transport*) pjmedia_stream_get_transport(pjmedia_stream *st) */ PJ_DEF(pj_status_t) pjmedia_stream_start(pjmedia_stream *stream) { - pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; PJ_ASSERT_RETURN(stream && c_strm->enc && c_strm->dec, PJ_EINVALIDOP); @@ -2602,7 +2602,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_reset_stat(pjmedia_stream *stream) PJ_DEF(pj_status_t) pjmedia_stream_get_stat_xr( const pjmedia_stream *stream, pjmedia_rtcp_xr_stat *stat) { - const pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + const pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL); @@ -2620,7 +2620,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_stat_xr( const pjmedia_stream *stream, PJ_DEF(pj_status_t) pjmedia_stream_get_stat_jbuf(const pjmedia_stream *stream, pjmedia_jb_state *state) { - const pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + const pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; PJ_ASSERT_RETURN(stream && state, PJ_EINVAL); return pjmedia_jbuf_get_state(c_strm->jb, state); @@ -2632,7 +2632,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_stat_jbuf(const pjmedia_stream *stream, PJ_DEF(pj_status_t) pjmedia_stream_pause( pjmedia_stream *stream, pjmedia_dir dir) { - pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; PJ_ASSERT_RETURN(stream, PJ_EINVAL); @@ -2662,7 +2662,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_pause( pjmedia_stream *stream, PJ_DEF(pj_status_t) pjmedia_stream_resume( pjmedia_stream *stream, pjmedia_dir dir) { - pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; PJ_ASSERT_RETURN(stream, PJ_EINVAL); @@ -2693,7 +2693,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf2( pjmedia_stream *stream, const pj_str_t *digit_char, unsigned duration) { - pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; pj_status_t status = PJ_SUCCESS; /* By convention we use jitter buffer mutex to access DTMF @@ -2786,7 +2786,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_dtmf( pjmedia_stream *stream, char *digits, unsigned *size) { - pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; PJ_ASSERT_RETURN(stream && digits && size, PJ_EINVAL); @@ -2823,7 +2823,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_set_dtmf_callback(pjmedia_stream *stream, int digit), void *user_data) { - pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; PJ_ASSERT_RETURN(stream, PJ_EINVAL); @@ -2846,7 +2846,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_set_dtmf_event_callback(pjmedia_stream *strea const pjmedia_stream_dtmf_event *event), void *user_data) { - pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; PJ_ASSERT_RETURN(stream, PJ_EINVAL); diff --git a/pjmedia/src/pjmedia/stream_imp_common.c b/pjmedia/src/pjmedia/stream_imp_common.c index 7b38541c2a..aa35c6983b 100755 --- a/pjmedia/src/pjmedia/stream_imp_common.c +++ b/pjmedia/src/pjmedia/stream_imp_common.c @@ -446,6 +446,8 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) /* Add ref counter to avoid premature destroy from callbacks */ pj_grp_lock_add_ref(c_strm->grp_lock); + pj_bzero(&seq_st, sizeof(seq_st)); + /* Ignore the packet if decoder is paused */ if (channel->paused) goto on_return; @@ -453,7 +455,6 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) /* Update RTP session (also checks if RTP session can accept * the incoming packet. */ - pj_bzero(&seq_st, sizeof(seq_st)); check_pt = PJMEDIA_STREAM_CHECK_RTP_PT; #ifdef AUDIO_STREAM check_pt = check_pt && hdr->pt != stream->rx_event_pt; diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c index c5ce745a3b..f36edadd56 100644 --- a/pjmedia/src/pjmedia/vid_stream.c +++ b/pjmedia/src/pjmedia/vid_stream.c @@ -1527,10 +1527,11 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( */ PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy( pjmedia_vid_stream *stream ) { - pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + pjmedia_stream_common *c_strm; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); + c_strm = &stream->base; PJ_LOG(4,(THIS_FILE, "Destroy request on %s..", c_strm->name.ptr)); /* Stop the streaming */ @@ -1610,7 +1611,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_port(pjmedia_vid_stream *stream, pjmedia_dir dir, pjmedia_port **p_port ) { - pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; PJ_ASSERT_RETURN(dir==PJMEDIA_DIR_ENCODING || dir==PJMEDIA_DIR_DECODING, PJ_EINVAL); @@ -1691,7 +1692,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_info( */ PJ_DEF(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream) { - pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; PJ_ASSERT_RETURN(stream && c_strm->enc && c_strm->dec, PJ_EINVALIDOP); @@ -1734,7 +1735,7 @@ pjmedia_vid_stream_modify_codec_param(pjmedia_vid_stream *stream, PJ_DEF(pj_bool_t) pjmedia_vid_stream_is_running(pjmedia_vid_stream *stream, pjmedia_dir dir) { - pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; pj_bool_t is_running = PJ_TRUE; PJ_ASSERT_RETURN(stream, PJ_FALSE); @@ -1756,7 +1757,7 @@ PJ_DEF(pj_bool_t) pjmedia_vid_stream_is_running(pjmedia_vid_stream *stream, PJ_DEF(pj_status_t) pjmedia_vid_stream_pause(pjmedia_vid_stream *stream, pjmedia_dir dir) { - pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; PJ_ASSERT_RETURN(stream, PJ_EINVAL); @@ -1786,7 +1787,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_pause(pjmedia_vid_stream *stream, PJ_DEF(pj_status_t) pjmedia_vid_stream_resume(pjmedia_vid_stream *stream, pjmedia_dir dir) { - pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; PJ_ASSERT_RETURN(stream, PJ_EINVAL); @@ -1860,7 +1861,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_bye( PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_pli( pjmedia_vid_stream *stream) { - pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; PJ_ASSERT_RETURN(stream, PJ_EINVAL); diff --git a/pjsip/include/pjsua2/account.hpp b/pjsip/include/pjsua2/account.hpp index 0eaa2b5f9b..bc4e2f1e99 100644 --- a/pjsip/include/pjsua2/account.hpp +++ b/pjsip/include/pjsua2/account.hpp @@ -800,6 +800,7 @@ struct AccountNatConfig : public PersistentObject iceAggressiveNomination(true), iceNominatedCheckDelayMsec(PJ_ICE_NOMINATED_CHECK_DELAY), iceWaitNominationTimeoutMsec(ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT), + iceCheckSrcAddr(PJ_ICE_SESS_CHECK_SRC_ADDR), iceNoRtcp(false), iceAlwaysUpdate(true), turnEnabled(false), diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index ba1819e8a4..fe4fe65fc5 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -3940,6 +3940,11 @@ static pj_status_t apply_med_update(pjsua_call_media *call_med, pjsua_stream_info stream_info; pj_str_t *enc_name = NULL; + /* Sanity check. */ + PJ_ASSERT_RETURN(call_med->type == PJMEDIA_TYPE_AUDIO || + call_med->type == PJMEDIA_TYPE_VIDEO, + PJ_EINVAL); + if (call_med->type == PJMEDIA_TYPE_AUDIO) { si = (pjmedia_stream_info_common *)&asi; status = pjmedia_stream_info_from_sdp( From 480d6837589c696a6983ac3fe224220f348ad9ef Mon Sep 17 00:00:00 2001 From: Dmitry Kolbik Date: Mon, 17 Feb 2025 06:08:38 +0100 Subject: [PATCH 201/491] Add TLS/SSL backend: Mbed TLS (#4295) --- aconfigure | 114 +++- aconfigure.ac | 46 +- pjlib/build/Makefile | 2 +- pjlib/include/pj/config.h | 3 + pjlib/src/pj/ssl_sock_mbedtls.c | 909 ++++++++++++++++++++++++++++++++ pjlib/src/pjlib-test/ssl_sock.c | 4 + 6 files changed, 1073 insertions(+), 5 deletions(-) create mode 100644 pjlib/src/pj/ssl_sock_mbedtls.c diff --git a/aconfigure b/aconfigure index cf7ece6db5..723f048e6d 100755 --- a/aconfigure +++ b/aconfigure @@ -668,6 +668,8 @@ opencore_amrnb_present opencore_amrnb_h_present ac_no_opencore_amrwb ac_no_opencore_amrnb +libmbedtls_present +mbedtls_h_present libgnutls_present gnutls_h_present libcrypto_present @@ -867,6 +869,7 @@ with_ipp_arch enable_android_mediacodec with_ssl with_gnutls +with_mbedtls enable_darwin_ssl enable_ssl with_opencore_amrnb @@ -1625,6 +1628,7 @@ Optional Packages: GnuTLS. To skip OpenSSL finding, use --with-gnutls option instead. --with-gnutls=DIR Specify alternate GnuTLS prefix + --with-mbedtls=DIR Specify alternate MbedTLS prefix --with-opencore-amrnb=DIR This option is obsolete and replaced by --with-opencore-amr=DIR @@ -9683,7 +9687,19 @@ esac fi -if test "x$ac_cross_compile" != "x" -a "x$with_ssl" = "xno" -a "x$with_gnutls" = "xno"; then + +# Check whether --with-mbedtls was given. +if test ${with_mbedtls+y} +then : + withval=$with_mbedtls; +else case e in #( + e) with_mbedtls=no + ;; +esac +fi + + +if test "x$ac_cross_compile" != "x" -a "x$with_ssl" = "xno" -a "x$with_gnutls" = "xno" -a "x$with_mbedtls" = "xno"; then enable_ssl=no fi @@ -9775,7 +9791,7 @@ else case e in #( printf "%s\n" "Using SSL prefix... $with_ssl" >&6; } fi - if test "x$with_gnutls" = "xno"; then + if test "x$with_gnutls" = "xno" -a "x$with_mbedtls" = "xno"; then # We still need to check for OpenSSL installations even if # we find Darwin SSL above since DTLS requires OpenSSL. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: checking for OpenSSL installations.." >&5 @@ -10139,6 +10155,100 @@ printf "%s\n" "** No GnuTLS libraries found, disabling SSL support **" >&6; } fi + if test "x$ac_ssl_backend" = "x"; then + if test "x$with_mbedtls" != "xno" -a "x$with_mbedtls" != "x"; then + CFLAGS="$CFLAGS -I$with_mbedtls/include" + LDFLAGS="$LDFLAGS -L$with_mbedtls/lib" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using MbedTLS prefix... $with_mbedtls" >&5 +printf "%s\n" "Using MbedTLS prefix... $with_mbedtls" >&6; } + fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: checking for MbedTLS installations.." >&5 +printf "%s\n" "checking for MbedTLS installations.." >&6; } + + + ac_fn_c_check_header_compile "$LINENO" "mbedtls/version.h" "ac_cv_header_mbedtls_version_h" "$ac_includes_default" +if test "x$ac_cv_header_mbedtls_version_h" = xyes +then : + mbedtls_h_present=1 +fi + + + if test "$PKG_CONFIG" != "none"; then + if $PKG_CONFIG --exists mbedtls mbedcrypto mbedx509; then + LIBS="$LIBS `$PKG_CONFIG --libs mbedtls mbedcrypto mbedx509`" + libmbedtls_present=1 + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for mbedtls_version_get_number in -lmbedtls" >&5 +printf %s "checking for mbedtls_version_get_number in -lmbedtls... " >&6; } +if test ${ac_cv_lib_mbedtls_mbedtls_version_get_number+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS +LIBS="-lmbedtls $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char mbedtls_version_get_number (void); +int +main (void) +{ +return mbedtls_version_get_number (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_mbedtls_mbedtls_version_get_number=yes +else case e in #( + e) ac_cv_lib_mbedtls_mbedtls_version_get_number=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_mbedtls_mbedtls_version_get_number" >&5 +printf "%s\n" "$ac_cv_lib_mbedtls_mbedtls_version_get_number" >&6; } +if test "x$ac_cv_lib_mbedtls_mbedtls_version_get_number" = xyes +then : + + libmbedtls_present=1 && LIBS="$LIBS -lmbedtls" +fi + + fi + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: *** Warning: neither pkg-config nor python is available, disabling mbedtls. ***" >&5 +printf "%s\n" "*** Warning: neither pkg-config nor python is available, disabling mbedtls. ***" >&6; } + fi + + if test "x$mbedtls_h_present" = "x1" -a "x$libmbedtls_present" = "x1"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: MbedTLS library found, SSL support enabled" >&5 +printf "%s\n" "MbedTLS library found, SSL support enabled" >&6; } + printf "%s\n" "#define PJ_HAS_SSL_SOCK 1" >>confdefs.h + + printf "%s\n" "#define PJ_SSL_SOCK_IMP PJ_SSL_SOCK_IMP_MBEDTLS" >>confdefs.h + + ac_ssl_backend="mbedtls" + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ** No MbedTLS libraries found, disabling SSL support **" >&5 +printf "%s\n" "** No MbedTLS libraries found, disabling SSL support **" >&6; } + fi + + fi + ;; esac fi diff --git a/aconfigure.ac b/aconfigure.ac index 7d304595e2..fdecf082bf 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -2106,8 +2106,15 @@ AC_ARG_WITH(gnutls, [with_gnutls=no] ) +dnl # MbedTLS alt prefix +AC_ARG_WITH(mbedtls, + AS_HELP_STRING([--with-mbedtls=DIR], [Specify alternate MbedTLS prefix]), + [], + [with_mbedtls=no] +) + dnl # Do not use default SSL installation if we are cross-compiling -if test "x$ac_cross_compile" != "x" -a "x$with_ssl" = "xno" -a "x$with_gnutls" = "xno"; then +if test "x$ac_cross_compile" != "x" -a "x$with_ssl" = "xno" -a "x$with_gnutls" = "xno" -a "x$with_mbedtls" = "xno"; then enable_ssl=no fi @@ -2174,7 +2181,7 @@ AC_ARG_ENABLE(ssl, AC_MSG_RESULT([Using SSL prefix... $with_ssl]) fi - if test "x$with_gnutls" = "xno"; then + if test "x$with_gnutls" = "xno" -a "x$with_mbedtls" = "xno"; then # We still need to check for OpenSSL installations even if # we find Darwin SSL above since DTLS requires OpenSSL. AC_MSG_RESULT([checking for OpenSSL installations..]) @@ -2264,6 +2271,41 @@ AC_ARG_ENABLE(ssl, fi fi + + if test "x$ac_ssl_backend" = "x"; then + if test "x$with_mbedtls" != "xno" -a "x$with_mbedtls" != "x"; then + CFLAGS="$CFLAGS -I$with_mbedtls/include" + LDFLAGS="$LDFLAGS -L$with_mbedtls/lib" + AC_MSG_RESULT([Using MbedTLS prefix... $with_mbedtls]) + fi + + AC_MSG_RESULT([checking for MbedTLS installations..]) + AC_SUBST(mbedtls_h_present) + AC_SUBST(libmbedtls_present) + AC_CHECK_HEADER(mbedtls/version.h, [mbedtls_h_present=1]) + + if test "$PKG_CONFIG" != "none"; then + if $PKG_CONFIG --exists mbedtls mbedcrypto mbedx509; then + LIBS="$LIBS `$PKG_CONFIG --libs mbedtls mbedcrypto mbedx509`" + libmbedtls_present=1 + else + AC_CHECK_LIB(mbedtls, mbedtls_version_get_number, [ + libmbedtls_present=1 && LIBS="$LIBS -lmbedtls"]) + fi + else + AC_MSG_RESULT([*** Warning: neither pkg-config nor python is available, disabling mbedtls. ***]) + fi + + if test "x$mbedtls_h_present" = "x1" -a "x$libmbedtls_present" = "x1"; then + AC_MSG_RESULT([MbedTLS library found, SSL support enabled]) + AC_DEFINE(PJ_HAS_SSL_SOCK, 1) + AC_DEFINE(PJ_SSL_SOCK_IMP, PJ_SSL_SOCK_IMP_MBEDTLS) + ac_ssl_backend="mbedtls" + else + AC_MSG_RESULT([** No MbedTLS libraries found, disabling SSL support **]) + fi + + fi ] ) diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile index caf01121d6..3d4bd7fd7e 100644 --- a/pjlib/build/Makefile +++ b/pjlib/build/Makefile @@ -36,7 +36,7 @@ export PJLIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ os_time_common.o os_info.o pool.o pool_buf.o pool_caching.o pool_dbg.o \ rand.o rbtree.o sock_common.o sock_qos_common.o \ ssl_sock_common.o ssl_sock_ossl.o ssl_sock_gtls.o ssl_sock_dump.o \ - ssl_sock_darwin.o string.o timer.o types.o unittest.o + ssl_sock_darwin.o ssl_sock_mbedtls.o string.o timer.o types.o unittest.o export PJLIB_CFLAGS += $(_CFLAGS) export PJLIB_CXXFLAGS += $(_CXXFLAGS) export PJLIB_LDFLAGS += $(_LDFLAGS) diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h index f4809ce788..d98254eb5e 100644 --- a/pjlib/include/pj/config.h +++ b/pjlib/include/pj/config.h @@ -1117,6 +1117,9 @@ /** Using Windows's Schannel */ #define PJ_SSL_SOCK_IMP_SCHANNEL 5 +/** Using Mbed TLS */ +#define PJ_SSL_SOCK_IMP_MBEDTLS 6 + /** * Select which SSL socket implementation to use. Currently pjlib supports * PJ_SSL_SOCK_IMP_OPENSSL, which uses OpenSSL, and PJ_SSL_SOCK_IMP_GNUTLS, diff --git a/pjlib/src/pj/ssl_sock_mbedtls.c b/pjlib/src/pj/ssl_sock_mbedtls.c new file mode 100644 index 0000000000..8b4d4388f1 --- /dev/null +++ b/pjlib/src/pj/ssl_sock_mbedtls.c @@ -0,0 +1,909 @@ +/* + * Copyright (C) 2025 Teluu Inc. (http://www.teluu.com) + * Copyright (c) 2025 Arlo Technologies, Inc. (https://www.arlo.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Only build when PJ_HAS_SSL_SOCK is enabled and when the backend is + * MbedTLS. + */ +#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \ + (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_MBEDTLS) + +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/debug.h" +#include "mbedtls/entropy.h" +#include "mbedtls/error.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/oid.h" +#include "mbedtls/platform.h" +#include "mbedtls/pk.h" +#include "mbedtls/ssl.h" +#include "mbedtls/version.h" + +#ifdef MBEDTLS_DEBUG_C +#define MBEDTLS_DEBUG_VERBOSE 1 +#endif + +#define SSL_SOCK_IMP_USE_CIRC_BUF + +#include "ssl_sock_imp_common.h" + +#define THIS_FILE "ssl_sock_mbedtls.c" + +/* + * Secure socket structure definition. + */ +typedef struct mbedtls_sock_t { + pj_ssl_sock_t base; + + mbedtls_ssl_context ssl_ctx; + mbedtls_ssl_config ssl_config; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_x509_crt cacert; + mbedtls_x509_crt cert; + mbedtls_pk_context pk_ctx; +} mbedtls_sock_t; + +#include "ssl_sock_imp_common.c" + +/* + ******************************************************************* + * Static/internal functions. + ******************************************************************* + */ + +/* MbedTLS way of reporting internal operations. */ +static void mbedtls_print_logs(void *ctx, int level, const char *file, + int line, const char *str) +{ + const char* last_slash; + const char* file_name; + + PJ_UNUSED_ARG(ctx); + PJ_UNUSED_ARG(level); + + last_slash = strrchr(file, '/'); + file_name = last_slash ? last_slash + 1 : file; + + PJ_LOG(3, (THIS_FILE, "%s:%d: %s", file_name, line, str)); +} + +/* Convert from MbedTLS error to pj_status_t. */ +static pj_status_t ssl_status_from_err(pj_ssl_sock_t *ssock, int err) +{ + pj_status_t status; + + PJ_UNUSED_ARG(ssock); + + switch (err) { + case 0: + status = PJ_SUCCESS; + break; + case MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL: + case MBEDTLS_ERR_SSL_ALLOC_FAILED: + status = PJ_ENOMEM; + break; + case MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE: + status = PJ_ETOOBIG; + break; + case MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE: + status = PJ_ENOTFOUND; + break; + case MBEDTLS_ERR_SSL_TIMEOUT: + status = PJ_ETIMEDOUT; + break; + case MBEDTLS_ERR_SSL_INTERNAL_ERROR: + case MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY: + status = PJ_EBUG; + break; + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: + case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS: + case MBEDTLS_ERR_SSL_RECEIVED_EARLY_DATA: + case MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET: + status = PJ_EPENDING; + break; + + case MBEDTLS_ERR_SSL_UNEXPECTED_RECORD: + case MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER: + case MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION: + case MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE: + status = PJ_EINVAL; + break; + default: + status = PJ_EUNKNOWN; + break; + } + + return status; +} + +static int ssl_data_push(void *ctx, const unsigned char *buf, size_t len) +{ + pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)ctx; + + pj_lock_acquire(ssock->circ_buf_output_mutex); + if (circ_write(&ssock->circ_buf_output, buf, len) != PJ_SUCCESS) { + pj_lock_release(ssock->circ_buf_output_mutex); + + return MBEDTLS_ERR_SSL_WANT_WRITE; + } + + pj_lock_release(ssock->circ_buf_output_mutex); + + return len; +} + +static int ssl_data_pull(void *ctx, unsigned char *buf, size_t len) +{ + pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)ctx; + pj_size_t circ_buf_size; + pj_size_t read_size; + + pj_lock_acquire(ssock->circ_buf_input_mutex); + + if (circ_empty(&ssock->circ_buf_input)) { + pj_lock_release(ssock->circ_buf_input_mutex); + + return MBEDTLS_ERR_SSL_WANT_READ; + } + + circ_buf_size = circ_size(&ssock->circ_buf_input); + read_size = PJ_MIN(circ_buf_size, len); + + circ_read(&ssock->circ_buf_input, buf, read_size); + pj_lock_release(ssock->circ_buf_input_mutex); + + return read_size; +} + +/* Get Common Name field string from a general name string */ +static void cert_get_cn(const pj_str_t *gen_name, pj_str_t *cn) +{ + pj_str_t CN_sign = {"CN=", 3}; + char *p, *q; + + pj_bzero(cn, sizeof(cn)); + + p = pj_strstr(gen_name, &CN_sign); + if (!p) + return; + + p += 3; /* shift pointer to value part */ + pj_strset(cn, p, gen_name->slen - (p - gen_name->ptr)); + q = pj_strchr(cn, ','); + if (q) + cn->slen = q - p; +} + +static void cert_get_time(pj_time_val *tv, + const mbedtls_x509_time *time, + pj_bool_t *gmt) +{ + pj_parsed_time pt; + + pt.day = time->day; + pt.mon = time->mon - 1; + pt.year = time->year; + + pt.sec = time->sec; + pt.min = time->min; + pt.hour = time->hour; + + pt.msec = 0; + + // Assume MbedTLS always use GMT + *gmt = PJ_TRUE; + + pj_time_encode(&pt, tv); +} + +static void cert_get_alt_name(const mbedtls_x509_crt *crt, + pj_pool_t *pool, + pj_ssl_cert_info *ci) +{ + const mbedtls_x509_sequence *cur; + size_t cnt_alt_name; + int ret; + + int is_alt_name = mbedtls_x509_crt_has_ext_type( + crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME); + if (!is_alt_name) + return; + + cnt_alt_name = 0; + cur = &crt->subject_alt_names; + while (cur != NULL) { + cur = cur->next; + cnt_alt_name++; + } + + ci->subj_alt_name.entry = pj_pool_calloc(pool, cnt_alt_name, + sizeof(*ci->subj_alt_name.entry)); + if (!ci->subj_alt_name.entry) { + PJ_LOG(2, (THIS_FILE, "Failed to allocate memory for SubjectAltName")); + return; + } + + ci->subj_alt_name.cnt = 0; + cur = &crt->subject_alt_names; + + while (cur != NULL) { + mbedtls_x509_subject_alternative_name san; + pj_ssl_cert_name_type type; + size_t len; + + memset(&san, 0, sizeof(san)); + ret = mbedtls_x509_parse_subject_alt_name(&cur->buf, &san); + if (ret != 0) { + cur = cur->next; + continue; + } + + len = san.san.unstructured_name.len; + switch (san.type) { + case MBEDTLS_X509_SAN_RFC822_NAME: + type = PJ_SSL_CERT_NAME_RFC822; + break; + case MBEDTLS_X509_SAN_DNS_NAME: + type = PJ_SSL_CERT_NAME_DNS; + break; + case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: + type = PJ_SSL_CERT_NAME_URI; + break; + case MBEDTLS_X509_SAN_IP_ADDRESS: + type = PJ_SSL_CERT_NAME_IP; + break; + default: + type = PJ_SSL_CERT_NAME_UNKNOWN; + break; + } + + if (len && type != PJ_SSL_CERT_NAME_UNKNOWN) { + ci->subj_alt_name.entry[ci->subj_alt_name.cnt].type = type; + if (type == PJ_SSL_CERT_NAME_IP) { + char ip_buf[PJ_INET6_ADDRSTRLEN]; + int af = pj_AF_INET(); + if (len == sizeof(pj_in6_addr)) + af = pj_AF_INET6(); + pj_inet_ntop2(af, san.san.unstructured_name.p, + ip_buf, sizeof(ip_buf)); + pj_strdup2(pool, + &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name, + ip_buf); + } else { + const pj_str_t str = {(char *)san.san.unstructured_name.p, len}; + pj_strdup(pool, + &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name, + &str); + } + ci->subj_alt_name.cnt++; + } + + /* So far memory is freed only in the case of directoryName + * parsing succeeding, as mbedtls_x509_get_name allocates memory. + */ + mbedtls_x509_free_subject_alt_name(&san); + cur = cur->next; + } +} + +static void update_cert_info(const mbedtls_x509_crt *crt, + pj_pool_t *pool, pj_ssl_cert_info *ci) +{ + int ret; + char buf[1024]; + size_t bufsize = sizeof(buf); + + pj_bzero(ci, sizeof(pj_ssl_cert_info)); + + ci->version = crt->version; + + /* Serial number */ + pj_memcpy(ci->serial_no, crt->serial.p, sizeof(ci->serial_no)); + + /* Issuer */ + ret = mbedtls_x509_dn_gets(buf, bufsize, &crt->issuer); + if (ret < 0) { + PJ_LOG(2, (THIS_FILE, "Error parsing cert issuer")); + return; + } + pj_strdup2(pool, &ci->issuer.info, buf); + cert_get_cn(&ci->issuer.info, &ci->issuer.cn); + + /* Subject */ + ret = mbedtls_x509_dn_gets(buf, bufsize, &crt->subject); + if (ret < 0) { + PJ_LOG(2, (THIS_FILE, "Error parsing cert subject")); + return; + } + pj_strdup2(pool, &ci->subject.info, buf); + cert_get_cn(&ci->subject.info, &ci->subject.cn); + + /* Validity period */ + cert_get_time(&ci->validity.start, &crt->valid_from, &ci->validity.gmt); + cert_get_time(&ci->validity.end, &crt->valid_to, &ci->validity.gmt); + + /* Subject Alternative Name extension */ + cert_get_alt_name(crt, pool, ci); +} + +static pj_status_t set_ssl_protocol(pj_ssl_sock_t *ssock) +{ + mbedtls_sock_t *mssock = (mbedtls_sock_t *)ssock; + mbedtls_ssl_protocol_version max_proto; + mbedtls_ssl_protocol_version min_proto; + + if (ssock->param.proto == PJ_SSL_SOCK_PROTO_DEFAULT) { + ssock->param.proto = PJ_SSL_SOCK_PROTO_TLS1_2 | + PJ_SSL_SOCK_PROTO_TLS1_3; + } + + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_3) { + max_proto = MBEDTLS_SSL_VERSION_TLS1_3; + } else if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_2) { + max_proto = MBEDTLS_SSL_VERSION_TLS1_2; + } else { + PJ_LOG(1, (THIS_FILE, "Unsupported TLS protocol")); + return PJ_EINVAL; + } + + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_2) { + min_proto = MBEDTLS_SSL_VERSION_TLS1_2; + } else { + min_proto = MBEDTLS_SSL_VERSION_TLS1_3; + } + + mbedtls_ssl_conf_min_tls_version(&mssock->ssl_config, min_proto); + mbedtls_ssl_conf_max_tls_version(&mssock->ssl_config, max_proto); + + return PJ_SUCCESS; +} + +static int cert_verify_cb(void *data, mbedtls_x509_crt *crt, + int depth, uint32_t *flags) +{ + pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)data; + const int cert_dump_log_level = 5; + + if (pj_log_get_level() >= cert_dump_log_level) { + char buf[1024]; + PJ_LOG(5, (THIS_FILE, "Certificate index in chain " + "- %d", depth)); + mbedtls_x509_crt_info(buf, sizeof(buf) - 1, "", crt); + PJ_LOG(5, (THIS_FILE, "Certificate info:\n%s", buf)); + } + + /* Peer cert depth = 0 */ + if (depth > 0) + return 0; + + if (((*flags) & MBEDTLS_X509_BADCERT_EXPIRED) != 0) { + PJ_LOG(3, (THIS_FILE, "Server certificate has expired!")); + ssock->verify_status |= PJ_SSL_CERT_EVALIDITY_PERIOD; + } + + if (((*flags) & MBEDTLS_X509_BADCERT_REVOKED) != 0) { + PJ_LOG(3, (THIS_FILE, "Server certificate has been revoked!")); + ssock->verify_status |= PJ_SSL_CERT_EREVOKED; + } + + if (((*flags) & MBEDTLS_X509_BADCERT_CN_MISMATCH) != 0) { + PJ_LOG(3, (THIS_FILE, "CN mismatch!")); + ssock->verify_status |= PJ_SSL_CERT_EISSUER_MISMATCH; + } + + if (((*flags) & MBEDTLS_X509_BADCERT_NOT_TRUSTED) != 0) { + PJ_LOG(3, (THIS_FILE, "Self-signed or not signed by a trusted CA!")); + ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED; + } + + if (((*flags) & MBEDTLS_X509_BADCRL_NOT_TRUSTED) != 0) { + PJ_LOG(3, (THIS_FILE, "CRL not trusted!")); + ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED; + } + + if (((*flags) & MBEDTLS_X509_BADCRL_EXPIRED) != 0) { + PJ_LOG(3, (THIS_FILE, "CRL expired!")); + ssock->verify_status |= PJ_SSL_CERT_EVALIDITY_PERIOD; + } + + if (((*flags) & MBEDTLS_X509_BADCERT_OTHER) != 0) { + PJ_LOG(3, (THIS_FILE, "other (unknown) flags!")); + ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN; + } + + if ((*flags) == 0) { + PJ_LOG(5, (THIS_FILE, "Certificate verified without error flags")); + } else { + update_cert_info(crt, ssock->pool, &ssock->remote_cert_info); + } + + return 0; +} + +static pj_status_t set_cert(pj_ssl_sock_t *ssock) +{ + mbedtls_sock_t *mssock = (mbedtls_sock_t *)ssock; + mbedtls_x509_crt *cacert_ = NULL; + int authmode = MBEDTLS_SSL_VERIFY_NONE; + int ret = 0; + pj_ssl_cert_t *cert = ssock->cert; + + if (cert == NULL) + goto on_config; + + if (cert->CA_buf.slen) { + ret = mbedtls_x509_crt_parse(&mssock->cacert, + (const unsigned char *)cert->CA_buf.ptr, + cert->CA_buf.slen); + if (ret != 0) { + PJ_LOG(1, (THIS_FILE, "Failed to CA mbedtls_x509_crt_parse, " + "ret = -0x%04X", -ret)); + goto on_error; + } + cacert_ = &mssock->cacert; + } + + if (cert->cert_buf.slen && cert->privkey_buf.slen) { + ret = mbedtls_x509_crt_parse(&mssock->cert, + (const unsigned char *)cert->cert_buf.ptr, + cert->cert_buf.slen); + if (ret != 0) { + PJ_LOG(1, (THIS_FILE, "Failed to mbedtls_x509_crt_parse, " + "ret = -0x%04X", -ret)); + goto on_error; + } + + ret = mbedtls_pk_parse_key(&mssock->pk_ctx, + (const unsigned char *)cert->privkey_buf.ptr, + cert->privkey_buf.slen, + (const unsigned char *)cert->privkey_pass.ptr, + cert->privkey_pass.slen, + mbedtls_ctr_drbg_random, &mssock->ctr_drbg); + if (ret != 0) { + PJ_LOG(1, (THIS_FILE, "Failed to mbedtls_pk_parse, " + "ret = -0x%04X", -ret)); + goto on_error; + } + + mbedtls_ssl_conf_own_cert(&mssock->ssl_config, + &mssock->cert, + &mssock->pk_ctx); + } + +#if defined(MBEDTLS_FS_IO) + if (cert->CA_file.slen) { + ret = mbedtls_x509_crt_parse_file(&mssock->cacert, cert->CA_file.ptr); + if (ret != 0) { + PJ_LOG(1, (THIS_FILE, "Failed to CA mbedtls_x509_crt_parse_file, " + "ret = -0x%04X", -ret)); + goto on_error; + } + cacert_ = &mssock->cacert; + } + + if (cert->CA_path.slen) { + ret = mbedtls_x509_crt_parse_path(&mssock->cacert, cert->CA_path.ptr); + if (ret != 0) { + PJ_LOG(1, (THIS_FILE, "Failed to CA mbedtls_x509_crt_parse_path, " + "ret = -0x%04X", -ret)); + goto on_error; + } + cacert_ = &mssock->cacert; + } + + if (cert->cert_file.slen && cert->privkey_file.slen) { + ret = mbedtls_x509_crt_parse_file(&mssock->cert, cert->cert_file.ptr); + if (ret != 0) { + PJ_LOG(1, (THIS_FILE, "Failed to mbedtls_x509_crt_parse_file, " + "ret = -0x%04X", -ret)); + goto on_error; + } + + ret = mbedtls_pk_parse_keyfile(&mssock->pk_ctx, + cert->privkey_file.ptr, + cert->privkey_pass.ptr, + mbedtls_ctr_drbg_random, + &mssock->ctr_drbg); + if (ret != 0) { + PJ_LOG(1, (THIS_FILE, "Failed to mbedtls_pk_parse_keyfile, " + "ret = -0x%04X", -ret)); + goto on_error; + } + + mbedtls_ssl_conf_own_cert(&mssock->ssl_config, + &mssock->cert, + &mssock->pk_ctx); + } +#endif + +on_config: + if (ssock->is_server) { + if (ssock->param.require_client_cert) { + authmode = MBEDTLS_SSL_VERIFY_REQUIRED; + } else { + authmode = MBEDTLS_SSL_VERIFY_OPTIONAL; + } + } else { + if (cacert_) + authmode = MBEDTLS_SSL_VERIFY_REQUIRED; + else + PJ_LOG(2, (THIS_FILE, "Peer validation is disabled")); + } + + mbedtls_ssl_conf_verify(&mssock->ssl_config, cert_verify_cb, ssock); + mbedtls_ssl_conf_ca_chain(&mssock->ssl_config, cacert_, NULL); + mbedtls_ssl_conf_authmode(&mssock->ssl_config, authmode); + +on_error: + return ssl_status_from_err(ssock, ret); +} + +static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock) +{ + mbedtls_sock_t *mssock = (mbedtls_sock_t *)ssock; + + /* mbedtls_ssl_conf_ciphersuites requires a 0-terminated + * list of supported ciphers + */ + if (ssock->param.ciphers_num > 0) { + unsigned i; + pj_ssl_cipher *ciphers; + ciphers = (pj_ssl_cipher*) + pj_pool_calloc(ssock->pool, ssock->param.ciphers_num + 1, + sizeof(pj_ssl_cipher)); + if (!ciphers) + return PJ_ENOMEM; + + for (i = 0; i < ssock->param.ciphers_num; ++i) + ciphers[i] = ssock->param.ciphers[i]; + + mbedtls_ssl_conf_ciphersuites(&mssock->ssl_config, ciphers); + } + + return PJ_SUCCESS; +} + +/* === SSL socket implementations === */ + +/* Allocate SSL backend struct */ +static pj_ssl_sock_t *ssl_alloc(pj_pool_t *pool) +{ + return (pj_ssl_sock_t *)PJ_POOL_ZALLOC_T(pool, mbedtls_sock_t); +} + +/* Create and initialize new SSL context and instance */ +static pj_status_t ssl_create(pj_ssl_sock_t *ssock) +{ + mbedtls_sock_t *mssock = (mbedtls_sock_t *)ssock; + const char *pers = "ssl_client"; + pj_status_t status; + int ret; + int endpoint; + /* This version string is 18 bytes long, as advised by version.h. */ + char version[18]; + + /* Suppress warnings */ + PJ_UNUSED_ARG(circ_reset); + PJ_UNUSED_ARG(circ_read_cancel); + PJ_UNUSED_ARG(get_ip_addr_ver); + + pj_assert(ssock); + + /* Initialize input circular buffer */ + status = circ_init(ssock->pool->factory, &ssock->circ_buf_input, 512); + if (status != PJ_SUCCESS) + return status; + + /* Initialize output circular buffer */ + status = circ_init(ssock->pool->factory, &ssock->circ_buf_output, 512); + if (status != PJ_SUCCESS) { + return status; + } + + mbedtls_version_get_string_full(version); + PJ_LOG(4, (THIS_FILE, "Mbed TLS version : %s", version)); + +#ifdef MBEDTLS_PSA_CRYPTO_C + ret = psa_crypto_init(); + if (ret != PSA_SUCCESS) { + PJ_LOG(1, (THIS_FILE, "Failed to initialize PSA Crypto, " + "ret = -0x%04X", ret)); + return PJ_EUNKNOWN; + } +#endif + + mbedtls_ssl_init(&mssock->ssl_ctx); + mbedtls_ssl_config_init(&mssock->ssl_config); + mbedtls_ctr_drbg_init(&mssock->ctr_drbg); + mbedtls_entropy_init(&mssock->entropy); + mbedtls_x509_crt_init(&mssock->cacert); + mbedtls_x509_crt_init(&mssock->cert); + mbedtls_pk_init(&mssock->pk_ctx); + + endpoint = ssock->is_server ? MBEDTLS_SSL_IS_SERVER + : MBEDTLS_SSL_IS_CLIENT; + ret = mbedtls_ssl_config_defaults(&mssock->ssl_config, endpoint, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if (ret != 0) { + PJ_LOG(1, (THIS_FILE, "Failed to mbedtls_ssl_config_defaults, " + "ret = -0x%04X", -ret)); + goto out; + } + + ret = mbedtls_ctr_drbg_seed(&mssock->ctr_drbg, mbedtls_entropy_func, + &mssock->entropy, + (const unsigned char *)pers, strlen(pers)); + if (ret != 0) { + PJ_LOG(1, (THIS_FILE, "Failed to mbedtls_ctr_drbg_seed, " + "ret = -0x%04X", -ret)); + goto out; + } + + mbedtls_ssl_conf_rng(&mssock->ssl_config, + mbedtls_ctr_drbg_random, + &mssock->ctr_drbg); + + status = set_ssl_protocol(ssock); + if (status != PJ_SUCCESS) + return status; + + status = set_cert(ssock); + if (status != PJ_SUCCESS) + return status; + + status = set_cipher_list(ssock); + if (status != PJ_SUCCESS) + return status; + +#if defined(MBEDTLS_DEBUG_C) + mbedtls_ssl_conf_dbg(&mssock->ssl_config, mbedtls_print_logs, NULL); + mbedtls_debug_set_threshold(MBEDTLS_DEBUG_VERBOSE); +#endif + + ret = mbedtls_ssl_setup(&mssock->ssl_ctx, &mssock->ssl_config); + if (ret != 0) { + PJ_LOG(1, (THIS_FILE, "Failed to mbedtls_ssl_setup, " + "ret = -0x%04X", -ret)); + goto out; + } + + if (!ssock->is_server && ssock->param.server_name.slen) { + ret = mbedtls_ssl_set_hostname(&mssock->ssl_ctx, + ssock->param.server_name.ptr); + if (ret != 0) { + PJ_LOG(1, (THIS_FILE, "Failed to mbedtls_ssl_set_hostname, " + "ret = -0x%04X", -ret)); + goto out; + } + } + + mbedtls_ssl_set_bio(&mssock->ssl_ctx, (void *)ssock, + (mbedtls_ssl_send_t *)ssl_data_push, + (mbedtls_ssl_recv_t *)ssl_data_pull, + NULL); + + ssl_ciphers_populate(); + + ret = 0; + +out: + return ssl_status_from_err(ssock, ret); +} + +/* Destroy MbedTLS credentials and session. */ +static void ssl_destroy(pj_ssl_sock_t *ssock) +{ + mbedtls_sock_t *mssock = (mbedtls_sock_t *)ssock; + + mbedtls_ssl_free(&mssock->ssl_ctx); + mbedtls_ssl_config_free(&mssock->ssl_config); + mbedtls_x509_crt_free(&mssock->cacert); + mbedtls_ctr_drbg_free(&mssock->ctr_drbg); + mbedtls_entropy_free(&mssock->entropy); + mbedtls_x509_crt_free(&mssock->cert); + mbedtls_pk_free(&mssock->pk_ctx); + +#ifdef MBEDTLS_PSA_CRYPTO_C + mbedtls_psa_crypto_free(); +#endif + + /* Destroy circular buffers */ + circ_deinit(&ssock->circ_buf_input); + circ_deinit(&ssock->circ_buf_output); +} + +/* Reset socket state. */ +static void ssl_reset_sock_state(pj_ssl_sock_t *ssock) +{ + pj_lock_acquire(ssock->circ_buf_output_mutex); + ssock->ssl_state = SSL_STATE_NULL; + pj_lock_release(ssock->circ_buf_output_mutex); + + ssl_close_sockets(ssock); +} + +static void ssl_ciphers_populate() +{ + const int *list; + + /* Populate once only */ + if (ssl_cipher_num) { + return; + } + + list = mbedtls_ssl_list_ciphersuites(); + + for (unsigned num = 0; + ssl_cipher_num < PJ_ARRAY_SIZE(ssl_ciphers) && list[num]; num++) { + const mbedtls_ssl_ciphersuite_t * const ciphersuite = + mbedtls_ssl_ciphersuite_from_id(list[num]); + if (!ciphersuite) + continue; + ssl_ciphers[ssl_cipher_num].name = + mbedtls_ssl_ciphersuite_get_name(ciphersuite); + ssl_ciphers[ssl_cipher_num].id = + mbedtls_ssl_ciphersuite_get_id(ciphersuite); + ssl_cipher_num++; + } +} + +static pj_ssl_cipher ssl_get_cipher(pj_ssl_sock_t *ssock) +{ + mbedtls_sock_t *mssock = (mbedtls_sock_t *)ssock; + + int id = mbedtls_ssl_get_ciphersuite_id_from_ssl(&mssock->ssl_ctx); + if (id != 0) { + return id; + } else { + return PJ_TLS_UNKNOWN_CIPHER; + } +} + +static void ssl_update_certs_info(pj_ssl_sock_t *ssock) +{ + mbedtls_sock_t *mssock = (mbedtls_sock_t *)ssock; + const mbedtls_x509_crt *crt; + + pj_assert(ssock->ssl_state == SSL_STATE_ESTABLISHED); + + /* Get active remote certificate */ + crt = mbedtls_ssl_get_peer_cert(&mssock->ssl_ctx); + if (crt) { + update_cert_info(crt, ssock->pool, &ssock->remote_cert_info); + } +} + +static void ssl_set_state(pj_ssl_sock_t *ssock, pj_bool_t is_server) +{ + PJ_UNUSED_ARG(ssock); + PJ_UNUSED_ARG(is_server); +} + +static void ssl_set_peer_name(pj_ssl_sock_t *ssock) +{ + /* Setting server name is done in ssl_create because ssl_create can handle + * errors returned from Mbed TLS APIs properly. + */ + PJ_UNUSED_ARG(ssock); +} + +/* Try to perform an asynchronous handshake */ +static pj_status_t ssl_do_handshake(pj_ssl_sock_t *ssock) +{ + mbedtls_sock_t *mssock = (mbedtls_sock_t *)ssock; + pj_status_t handshake_status; + pj_status_t status; + int ret; + + ret = mbedtls_ssl_handshake(&mssock->ssl_ctx); + handshake_status = ssl_status_from_err(ssock, ret); + + status = flush_circ_buf_output(ssock, &ssock->handshake_op_key, 0, 0); + if (status != PJ_SUCCESS) { + PJ_LOG(2, (THIS_FILE, "Failed to send handshake packets")); + return status; + } + if (handshake_status == PJ_EPENDING) + return PJ_EPENDING; + + + if (handshake_status != PJ_SUCCESS) { + PJ_LOG(2, (THIS_FILE, "Failed to mbedtls_ssl_handshake, " + "ret -0x%04X", -ret)); + return handshake_status; + } + + ssock->ssl_state = SSL_STATE_ESTABLISHED; + + return PJ_SUCCESS; +} + +static pj_status_t ssl_renegotiate(pj_ssl_sock_t *ssock) +{ + PJ_UNUSED_ARG(ssock); + + return PJ_SUCCESS; +} + +static pj_status_t ssl_read(pj_ssl_sock_t *ssock, void *data, int *size) +{ + mbedtls_sock_t *mssock = (mbedtls_sock_t *)ssock; + + int ret = mbedtls_ssl_read(&mssock->ssl_ctx, data, *size); + if (ret >= 0) { + *size = ret; + return PJ_SUCCESS; + } else if (ret == MBEDTLS_ERR_SSL_WANT_READ) { + *size = 0; + return PJ_SUCCESS; + } else { + return PJ_EUNKNOWN; + } +} + +static pj_status_t ssl_write(pj_ssl_sock_t *ssock, const void *data, + pj_ssize_t size, int *nwritten) +{ + mbedtls_sock_t *mssock = (mbedtls_sock_t *)ssock; + pj_status_t status; + pj_ssize_t nwritten_ = 0; + int ret; + + while (nwritten_ < size) { + ret = mbedtls_ssl_write(&mssock->ssl_ctx, + ((const unsigned char *)data) + nwritten_, + size - nwritten_); + if (ret < 0) { + status = ssl_status_from_err(ssock, ret); + + if (status == PJ_EPENDING) + continue; + + *nwritten = nwritten_; + PJ_LOG(2, (THIS_FILE, "Failed to mbedtls_ssl_write, " + "ret -0x%04X", -ret)); + return status; + } + + nwritten_ += ret; + } + + *nwritten = nwritten_; + return PJ_SUCCESS; +} + +#endif /* PJ_HAS_SSL_SOCK */ diff --git a/pjlib/src/pjlib-test/ssl_sock.c b/pjlib/src/pjlib-test/ssl_sock.c index 2ca31e9664..b3cbbb99fc 100644 --- a/pjlib/src/pjlib-test/ssl_sock.c +++ b/pjlib/src/pjlib-test/ssl_sock.c @@ -1718,10 +1718,14 @@ int ssl_sock_test(void) #endif #if WITH_BENCHMARK +#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_MBEDTLS) PJ_LOG(3,("", "..performance test")); ret = perf_test(PJ_IOQUEUE_MAX_HANDLES/2 - 1, 0); if (ret != 0) return ret; +#else + PJ_UNUSED_ARG(perf_test); +#endif #endif PJ_LOG(3,("", "..client non-SSL (handshake timeout 5 secs)")); From 149d0a8fcba0c9c1094f14e221eacbf6f17aa375 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Mon, 17 Feb 2025 15:59:41 +0700 Subject: [PATCH 202/491] Fix errors in building for shared libs (#4306) --- third_party/build/g7221/Makefile | 1 + third_party/build/gsm/Makefile | 1 + third_party/build/ilbc/Makefile | 1 + third_party/build/milenage/Makefile | 1 + third_party/build/webrtc/Makefile | 1 + third_party/build/webrtc_aec3/Makefile | 1 + third_party/build/yuv/Makefile | 1 + 7 files changed, 7 insertions(+) diff --git a/third_party/build/g7221/Makefile b/third_party/build/g7221/Makefile index d0e7596413..f4e5ca3243 100644 --- a/third_party/build/g7221/Makefile +++ b/third_party/build/g7221/Makefile @@ -32,6 +32,7 @@ export G7221_CODEC_OBJS = common/common.o common/huff_tab.o common/tables.o \ encode/dct4_a.o encode/sam2coef.o encode/encoder.o export G7221_CODEC_CFLAGS = $(_CFLAGS) +export G7221_CODEC_LDFLAGS = $(_LDFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT diff --git a/third_party/build/gsm/Makefile b/third_party/build/gsm/Makefile index 0b949982e3..cf027f7236 100644 --- a/third_party/build/gsm/Makefile +++ b/third_party/build/gsm/Makefile @@ -34,6 +34,7 @@ export GSM_CODEC_OBJS = add.o code.o decode.o \ export GSM_CODEC_CFLAGS = -DSASR -DWAV49 -DNeedFunctionPrototypes=1 $(_CFLAGS) +export GSM_CODEC_LDFLAGS += $(_LDFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### diff --git a/third_party/build/ilbc/Makefile b/third_party/build/ilbc/Makefile index 39ac6b2b0d..28023a56ad 100644 --- a/third_party/build/ilbc/Makefile +++ b/third_party/build/ilbc/Makefile @@ -34,6 +34,7 @@ export ILBC_OBJS = FrameClassify.o LPCdecode.o LPCencode.o \ iLBC_decode.o iLBC_encode.o lsf.o \ packing.o syntFilter.o export ILBC_CFLAGS = $(_CFLAGS) +export ILBC_LDFLAGS = $(_LDFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT diff --git a/third_party/build/milenage/Makefile b/third_party/build/milenage/Makefile index 18a48b91af..7c3202eb11 100644 --- a/third_party/build/milenage/Makefile +++ b/third_party/build/milenage/Makefile @@ -27,6 +27,7 @@ export _LDFLAGS := $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ export MILENAGE_SRCDIR = ../../milenage export MILENAGE_OBJS = milenage.o rijndael.o export MILENAGE_CFLAGS = $(_CFLAGS) +export MILENAGE_LDFLAGS = $(_LDFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT diff --git a/third_party/build/webrtc/Makefile b/third_party/build/webrtc/Makefile index 391e7786ab..3db223ee28 100644 --- a/third_party/build/webrtc/Makefile +++ b/third_party/build/webrtc/Makefile @@ -61,6 +61,7 @@ export WEBRTC_OBJS = \ export WEBRTC_CFLAGS = $(_CFLAGS) $(WEBRTC_OTHER_CFLAGS) export WEBRTC_CXXFLAGS = $(WEBRTC_CFLAGS) +export WEBRTC_LDFLAGS = $(_LDFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT diff --git a/third_party/build/webrtc_aec3/Makefile b/third_party/build/webrtc_aec3/Makefile index 95b445d8f8..016219aef3 100644 --- a/third_party/build/webrtc_aec3/Makefile +++ b/third_party/build/webrtc_aec3/Makefile @@ -168,6 +168,7 @@ export WEBRTC_AEC3_OBJS = \ export WEBRTC_AEC3_CFLAGS = $(_CFLAGS) $(WEBRTC_AEC3_OTHER_CFLAGS) export WEBRTC_AEC3_CXXFLAGS = $(WEBRTC_AEC3_CFLAGS) $(_CXXFLAGS) +export WEBRTC_AEC3_LDFLAGS = $(_LDFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT diff --git a/third_party/build/yuv/Makefile b/third_party/build/yuv/Makefile index 6d97e7f10b..7b11094fdc 100644 --- a/third_party/build/yuv/Makefile +++ b/third_party/build/yuv/Makefile @@ -72,6 +72,7 @@ export YUV_OBJS = \ export YUV_CFLAGS = -fomit-frame-pointer -fno-strict-aliasing -Wno-memset-elt-size -Wno-unknown-warning-option -Wno-pragmas $(_CFLAGS) export YUV_CXXFLAGS = -fomit-frame-pointer -fno-strict-aliasing -Wno-memset-elt-size -Wno-unknown-warning-option -Wno-pragmas $(_CXXFLAGS) +export YUV_LDFLAGS = $(_LDFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT From 8e979ddfbb4f723a06dfe69360733ebaa8626360 Mon Sep 17 00:00:00 2001 From: Amilcar Ubiera Date: Mon, 17 Feb 2025 23:22:36 -0500 Subject: [PATCH 203/491] pjsua_media: Fix to apply_med_update when PJMEDIA_HAS_VIDEO is disabled. (#4308) --- pjsip/src/pjsua-lib/pjsua_media.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index fe4fe65fc5..e4b2d44c9a 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -3940,11 +3940,6 @@ static pj_status_t apply_med_update(pjsua_call_media *call_med, pjsua_stream_info stream_info; pj_str_t *enc_name = NULL; - /* Sanity check. */ - PJ_ASSERT_RETURN(call_med->type == PJMEDIA_TYPE_AUDIO || - call_med->type == PJMEDIA_TYPE_VIDEO, - PJ_EINVAL); - if (call_med->type == PJMEDIA_TYPE_AUDIO) { si = (pjmedia_stream_info_common *)&asi; status = pjmedia_stream_info_from_sdp( @@ -3963,6 +3958,9 @@ static pj_status_t apply_med_update(pjsua_call_media *call_med, enc_name = &vsi.codec_info.encoding_name; #endif } + else { + status = PJMEDIA_EUNSUPMEDIATYPE; + } if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, From 940e043ab58ae4258470c3dfec3fe02515e7de2c Mon Sep 17 00:00:00 2001 From: Amilcar Ubiera Date: Tue, 18 Feb 2025 00:21:17 -0500 Subject: [PATCH 204/491] ssl_sock_mbedtls: Fix to "incompatible types" warning when calling mbedtls_ssl_conf_ciphersuites. (#4309) --- pjlib/src/pj/ssl_sock_mbedtls.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pjlib/src/pj/ssl_sock_mbedtls.c b/pjlib/src/pj/ssl_sock_mbedtls.c index 8b4d4388f1..b75568e150 100644 --- a/pjlib/src/pj/ssl_sock_mbedtls.c +++ b/pjlib/src/pj/ssl_sock_mbedtls.c @@ -580,15 +580,15 @@ static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock) */ if (ssock->param.ciphers_num > 0) { unsigned i; - pj_ssl_cipher *ciphers; - ciphers = (pj_ssl_cipher*) + int *ciphers; + ciphers = (int*) pj_pool_calloc(ssock->pool, ssock->param.ciphers_num + 1, - sizeof(pj_ssl_cipher)); + sizeof(int)); if (!ciphers) return PJ_ENOMEM; for (i = 0; i < ssock->param.ciphers_num; ++i) - ciphers[i] = ssock->param.ciphers[i]; + ciphers[i] = (int)ssock->param.ciphers[i]; mbedtls_ssl_conf_ciphersuites(&mssock->ssl_config, ciphers); } From a57f587226f1626c10da85fd9e665747df4d8839 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 18 Feb 2025 12:39:35 +0700 Subject: [PATCH 205/491] Fix and update PJMEDIA_TRANSPORT_SWITCH_REMOTE_ADDR docs (#4310) - The docs is not shown due to a wrong Doxygen format. - Also updated the docs. --- pjmedia/include/pjmedia/config.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h index 231fd7aaaa..f75029aa0a 100644 --- a/pjmedia/include/pjmedia/config.h +++ b/pjmedia/include/pjmedia/config.h @@ -1234,8 +1234,14 @@ #endif -/* Setting to determine if media transport should switch RTP and RTCP +/** + * Setting to determine if media transport should switch RTP and RTCP * remote address to the source address of the packets it receives. + * This feature is usually used for handling NAT traversal issues, + * also known as symmetric RTP and 'latching' techniques. + * + * See also run-time options #PJMEDIA_UDP_NO_SRC_ADDR_CHECKING and + * #PJMEDIA_ICE_NO_SRC_ADDR_CHECKING. * * By default it is enabled. */ From 6337fe394b7786af2f64cbc236993da649543f32 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 18 Feb 2025 12:39:57 +0700 Subject: [PATCH 206/491] Avoid deadlock in SIP transport shutdown (#4307) --- pjsip/src/pjsip/sip_transport.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index 7ce617d4c7..4ef55843d9 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -1466,11 +1466,14 @@ PJ_DEF(pj_status_t) pjsip_transport_shutdown2(pjsip_transport *tp, pj_lock_acquire(tp->lock); mgr = tp->tpmgr; - pj_lock_acquire(mgr->lock); + + // Acquiring manager lock after transport lock may cause deadlock. + // And it does not seem necessary as well. + //pj_lock_acquire(mgr->lock); /* Do nothing if transport is being shutdown/destroyed already */ if (tp->is_shutdown || tp->is_destroying) { - pj_lock_release(mgr->lock); + //pj_lock_release(mgr->lock); pj_lock_release(tp->lock); return PJ_SUCCESS; } @@ -1501,7 +1504,7 @@ PJ_DEF(pj_status_t) pjsip_transport_shutdown2(pjsip_transport *tp, pjsip_transport_dec_ref(tp); } - pj_lock_release(mgr->lock); + //pj_lock_release(mgr->lock); pj_lock_release(tp->lock); return status; From 577d82b71c0b976cca70762783109825bbd68ef0 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 20 Feb 2025 09:38:33 +0700 Subject: [PATCH 207/491] Fix warnings by coverity & by msvc (#4315) Fix various warnings, e.g: unreachable code, uninitialized vars, unused var, signed/unsigned comparison --- pjsip/src/pjsip/sip_auth_client.c | 2 +- pjsip/src/pjsip/sip_transport.c | 2 +- pjsip/src/pjsip/sip_transport_tls.c | 3 ++- pjsip/src/pjsip/sip_util.c | 10 +++++++--- pjsip/src/pjsua-lib/pjsua_media.c | 15 +++++++++------ 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c index 31bf551e68..f50c489a62 100644 --- a/pjsip/src/pjsip/sip_auth_client.c +++ b/pjsip/src/pjsip/sip_auth_client.c @@ -108,7 +108,7 @@ const pjsip_auth_algorithm pjsip_auth_algorithms[] = { #define DO_ON_PARENT_LOCKED(sess, call) \ do { \ - pj_status_t on_parent; \ + pj_status_t on_parent = PJ_SUCCESS; \ pj_bool_t with_parent = PJ_FALSE; \ if (sess->parent) { \ pj_lock_acquire(sess->parent->lock); \ diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index 4ef55843d9..6ccf410959 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -1488,7 +1488,7 @@ PJ_DEF(pj_status_t) pjsip_transport_shutdown2(pjsip_transport *tp, tp->is_shutdown = PJ_TRUE; /* Notify application of transport shutdown */ - state_cb = pjsip_tpmgr_get_state_cb(tp->tpmgr); + state_cb = pjsip_tpmgr_get_state_cb(mgr); if (state_cb) { pjsip_transport_state_info state_info; diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c index 2079351979..18f92eaca9 100644 --- a/pjsip/src/pjsip/sip_transport_tls.c +++ b/pjsip/src/pjsip/sip_transport_tls.c @@ -931,9 +931,10 @@ static pj_status_t tls_create( struct tls_listener *listener, } if (addr) { + unsigned i; pj_memcpy( &tls->server_addr, addr, sizeof(pjsip_server_addresses)); - for (int i = 0; i < addr->count; ++i) { + for (i = 0; i < addr->count; ++i) { pj_strdup(pool, &tls->server_addr.entry[i].name, &addr->entry[i].name); } } diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c index 076e643ecf..b5f64a7ff0 100644 --- a/pjsip/src/pjsip/sip_util.c +++ b/pjsip/src/pjsip/sip_util.c @@ -1369,9 +1369,10 @@ stateless_send_resolver_callback( pj_status_t status, /* Copy server addresses */ if (addr && addr != &tdata->dest_info.addr) { + unsigned i; pj_memcpy( &tdata->dest_info.addr, addr, sizeof(pjsip_server_addresses)); - for (int i = 0; i < addr->count; ++i) { + for (i = 0; i < addr->count; ++i) { pj_strdup(tdata->pool, &tdata->dest_info.addr.entry[i].name, &addr->entry[i].name); } } @@ -1804,6 +1805,7 @@ static void send_response_resolver_cb( pj_status_t status, void *token, const pjsip_server_addresses *addr ) { pjsip_send_state *send_state = (pjsip_send_state*) token; + unsigned i; if (status != PJ_SUCCESS) { if (send_state->app_cb) { @@ -1835,8 +1837,10 @@ static void send_response_resolver_cb( pj_status_t status, void *token, /* Update address in send_state. */ pj_memcpy(&send_state->tdata->dest_info.addr, addr, sizeof(*addr)); - for (int i = 0; i < send_state->tdata->dest_info.addr.count; ++i) { - pj_strdup(send_state->tdata->pool, &send_state->tdata->dest_info.addr.entry[i].name, &addr->entry[i].name); + for (i = 0; i < send_state->tdata->dest_info.addr.count; ++i) { + pj_strdup(send_state->tdata->pool, + &send_state->tdata->dest_info.addr.entry[i].name, + &addr->entry[i].name); } /* Send response using the transoprt. */ diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index e4b2d44c9a..1fcbef68ae 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -3932,12 +3932,16 @@ static pj_status_t apply_med_update(pjsua_call_media *call_med, pj_pool_t *tmp_pool = call->inv->pool_prov; pj_status_t status = PJ_SUCCESS; - pjmedia_stream_info asi; + /* Initialize the following variables to avoid warnings from compiler + * and code analysis. Actually the function will always init the vars + * when the media type is valid/recognized. + */ + pjmedia_stream_info asi = {0}; #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) - pjmedia_vid_stream_info vsi; + pjmedia_vid_stream_info vsi = {0}; #endif - pjmedia_stream_info_common *si; - pjsua_stream_info stream_info; + pjmedia_stream_info_common *si = NULL; + pjsua_stream_info stream_info = {0}; pj_str_t *enc_name = NULL; if (call_med->type == PJMEDIA_TYPE_AUDIO) { @@ -4231,8 +4235,7 @@ static pj_status_t apply_med_update(pjsua_call_media *call_med, } len = pj_ansi_snprintf( info+info_len, sizeof(info)-info_len, ", stream #%d: %.*s (%s)", mi, - (enc_name? (int)enc_name->slen: 0), - (enc_name? enc_name->ptr: info), + (int)enc_name->slen, enc_name->ptr, dir); if (len > 0) info_len += len; From 5ce1ba679da0f08652a742b51a02652a9d7fdec7 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Fri, 21 Feb 2025 09:02:09 +0700 Subject: [PATCH 208/491] Update media change detection for H264 (#4317) --- pjsip/src/pjsua-lib/pjsua_media.c | 38 +++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index 1fcbef68ae..ce988d26df 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -18,6 +18,7 @@ */ #include #include +#include #define THIS_FILE "pjsua_media.c" @@ -3876,15 +3877,42 @@ static pj_bool_t is_media_changed(const pjsua_call *call, return PJ_TRUE; } - /* Compare codec param */ - if (/* old_cp->enc_mtu != new_cp->enc_mtu || */ - pj_memcmp(&old_cp->enc_fmt.det, &new_cp->enc_fmt.det, - sizeof(pjmedia_video_format_detail)) || - !match_codec_fmtp(&old_cp->dec_fmtp, &new_cp->dec_fmtp) || + /* Compare SDP fmtp for both directions */ + if (!match_codec_fmtp(&old_cp->dec_fmtp, &new_cp->dec_fmtp) || !match_codec_fmtp(&old_cp->enc_fmtp, &new_cp->enc_fmtp)) { return PJ_TRUE; } + + /* Compare format for encoding only */ + if (pj_memcmp(&old_cp->enc_fmt.det, &new_cp->enc_fmt.det, + sizeof(pjmedia_video_format_detail))) + { + /* For H264, resolution may be adjusted to remote's profile-level, + * so check if it is different because of the adjusted format. + */ + if (pj_strcmp2(&new_ci->encoding_name, "H264") == 0) { + pjmedia_vid_codec_param param = {0}; + pj_status_t status; + + param.dir = PJMEDIA_DIR_ENCODING; + pj_memcpy(¶m.enc_fmt, &new_cp->enc_fmt, + sizeof(param.enc_fmt)); + pj_memcpy(¶m.enc_fmtp,&new_cp->enc_fmtp, + sizeof(param.enc_fmtp)); + status = pjmedia_vid_codec_h264_apply_fmtp(¶m); + if (status != PJ_SUCCESS) + return PJ_TRUE; + + if (pj_memcmp(&old_cp->enc_fmt.det, ¶m.enc_fmt.det, + sizeof(pjmedia_video_format_detail))) + { + return PJ_TRUE; + } + } else { + return PJ_TRUE; + } + } } #endif From f3b82d6fdd4666771ddfbc182177e521578412f2 Mon Sep 17 00:00:00 2001 From: sauwming Date: Tue, 25 Feb 2025 17:45:07 +0800 Subject: [PATCH 209/491] Add compile-time option to specify if DNS resolver should discard truncated answer (#4324) --- pjlib-util/include/pjlib-util/config.h | 10 ++++++++++ pjlib-util/src/pjlib-util/srv_resolver.c | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/pjlib-util/include/pjlib-util/config.h b/pjlib-util/include/pjlib-util/config.h index 46b4d5de44..375746b0fa 100644 --- a/pjlib-util/include/pjlib-util/config.h +++ b/pjlib-util/include/pjlib-util/config.h @@ -195,6 +195,16 @@ #endif +/** + * Specifies whether DNS resolver should discard truncated answer. + * + * Default: 1 (yes) + */ +#ifndef PJ_DNS_RESOLVER_DISCARD_TRUNCATED_ANSWER +# define PJ_DNS_RESOLVER_DISCARD_TRUNCATED_ANSWER 1 +#endif + + /* ************************************************************************** * SCANNER CONFIGURATION */ diff --git a/pjlib-util/src/pjlib-util/srv_resolver.c b/pjlib-util/src/pjlib-util/srv_resolver.c index 675fadfc80..fd4ab91000 100644 --- a/pjlib-util/src/pjlib-util/srv_resolver.c +++ b/pjlib-util/src/pjlib-util/srv_resolver.c @@ -590,7 +590,9 @@ static void dns_callback(void *user_data, query_job->q_srv = NULL; if (status == PJ_SUCCESS) { - if (PJ_DNS_GET_TC(pkt->hdr.flags)) { + if (PJ_DNS_RESOLVER_DISCARD_TRUNCATED_ANSWER && + PJ_DNS_GET_TC(pkt->hdr.flags)) + { /* Got truncated answer, the standard recommends to follow up * the query using TCP. Since we currently don't support it, * just return error. From 398bc21c55965b1e2384b99f4a9fa7c2d04b2b20 Mon Sep 17 00:00:00 2001 From: Riza Sulistyo Date: Wed, 26 Feb 2025 11:00:20 +0700 Subject: [PATCH 210/491] Add logging information when incoming message is ignored caused by unchecked source address (#4321) * Check if remote candidate is valid even after ICE is completed * Modification based on comments - Add log that the ignored incoming message can be caused that it is unchecked * Revert the comment * add comment --- pjnath/src/pjnath/ice_session.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c index d751c04ffb..77eec0aa73 100644 --- a/pjnath/src/pjnath/ice_session.c +++ b/pjnath/src/pjnath/ice_session.c @@ -3745,6 +3745,11 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, if (!pj_sockaddr_has_addr(raddr)) { for (i = 0; i < ice->rcand_cnt; ++i) { + /* Make sure that the source address is part of the remote + * candidate and it is a valid (already checked). + * Note that the candidate will not be set to valid if the + * incoming check is received after the ICE is completed. + */ if (ice->rcand[i].comp_id == comp_id && ice->rcand[i].checked && pj_sockaddr_cmp(src_addr, &ice->rcand[i].addr) == 0) @@ -3783,7 +3788,8 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, pj_sockaddr_print(src_addr, paddr, sizeof(paddr), 3); PJ_LOG(4, (ice->obj_name, "Ignoring incoming message for " - "component [%d] because source addr %s unrecognized", + "component [%d] because source addr %s unrecognized " + "or unchecked", comp_id, paddr)); return PJ_SUCCESS; } From 19ce099bbf11f5c72fb30466246a04779487038c Mon Sep 17 00:00:00 2001 From: thanhloi2603 <42855486+thanhloi2603@users.noreply.github.com> Date: Mon, 3 Mar 2025 05:58:48 +0700 Subject: [PATCH 211/491] Port dlg event subscription (#4328) --- pjsip/include/pjsua2/endpoint.hpp | 5 +++ pjsip/include/pjsua2/presence.hpp | 57 ++++++++++++++++++++++++++++++- pjsip/src/pjsua-lib/pjsua_pres.c | 4 +++ pjsip/src/pjsua2/endpoint.cpp | 31 +++++++++++++++++ pjsip/src/pjsua2/presence.cpp | 22 ++++++++++-- 5 files changed, 116 insertions(+), 3 deletions(-) diff --git a/pjsip/include/pjsua2/endpoint.hpp b/pjsip/include/pjsua2/endpoint.hpp index cb78cc3b6c..2139579db3 100644 --- a/pjsip/include/pjsua2/endpoint.hpp +++ b/pjsip/include/pjsua2/endpoint.hpp @@ -2084,6 +2084,11 @@ class Endpoint static void on_buddy_evsub_state(pjsua_buddy_id buddy_id, pjsip_evsub *sub, pjsip_event *event); + static void on_buddy_dlg_event_state(pjsua_buddy_id buddy_id); + static void on_buddy_evsub_dlg_event_state(pjsua_buddy_id buddy_id, + pjsip_evsub *sub, + pjsip_event *event); + // Call callbacks static void on_call_state(pjsua_call_id call_id, pjsip_event *e); static void on_call_tsx_state(pjsua_call_id call_id, diff --git a/pjsip/include/pjsua2/presence.hpp b/pjsip/include/pjsua2/presence.hpp index 0145eed0ab..1951cb049a 100644 --- a/pjsip/include/pjsua2/presence.hpp +++ b/pjsip/include/pjsua2/presence.hpp @@ -93,6 +93,14 @@ struct BuddyConfig : public PersistentObject */ bool subscribe; + /** + * Specify whether we should immediately subscribe to the buddy's + * dialog event, such as for Busy Lamp Field (BLF) feature. + * Note that only one subscription (presence or dialog event) + * can be active at any time. + */ + bool subscribe_dlg_event; + public: /** * Read this object from a container node. @@ -302,6 +310,40 @@ class Buddy */ void updatePresence(void) PJSUA2_THROW(Error); + /** + * Enable/disable buddy's dialog event monitoring. Once buddy's dialog event + * is subscribed, application will be informed about buddy's dialog info + * status change via \a onBuddyDlgEventState() callback. + * + * Note that only one subscription (presence or dialog event) can be active + * at any time. + * + * @param subscribe Specify non-zero to activate dialog event subscription + * to the specified buddy. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ + void subscribeDlgEvent(bool subscribe) PJSUA2_THROW(Error); + + /** + * Update the dialog event information for the buddy. Although the library + * periodically refreshes the dialog event subscription for all buddies, some + * application may want to refresh the buddy's dialog event subscription + * immediately, and in this case it can use this function to accomplish + * this. + * + * Note that the buddy's dialog event subscription will only be initiated + * if dialog event monitoring is enabled for the buddy. See + * subscribeDlgEvent() for more info. Also if dialog event + * subscription for the buddy is already active, this function will not do + * anything. + * + * Once the dialog event subscription is activated successfully for the buddy, + * application will be notified about the buddy's dialog info status in the + * onBuddyDlgEventState() callback. + */ + void updateDlgEvent(void) PJSUA2_THROW(Error); + /** * Send instant messaging outside dialog, using this buddy's specified * account for route set and authentication. @@ -330,6 +372,12 @@ class Buddy */ virtual void onBuddyState() {} + /** + * Notify application when the buddy dialog state has changed. + * Application may then query the buddy into to get the details. + */ + virtual void onBuddyDlgEventState() + {} /** * Notify application when the state of client subscription session @@ -341,7 +389,14 @@ class Buddy */ virtual void onBuddyEvSubState(OnBuddyEvSubStateParam &prm) { PJ_UNUSED_ARG(prm); } - + /** + * Notify application when the state of client subscription session + * associated with a buddy dialog state has changed. Application + * may use this callback to retrieve more detailed information about the + * state changed event. + */ + virtual void onBuddyEvSubDlgEventState(OnBuddyEvSubStateParam &prm) + { PJ_UNUSED_ARG(prm); } private: /** * Buddy ID. diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c index 3a6fbc3888..2f3b8b7625 100644 --- a/pjsip/src/pjsua-lib/pjsua_pres.c +++ b/pjsip/src/pjsua-lib/pjsua_pres.c @@ -675,6 +675,10 @@ PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id buddy_id, return PJ_SUCCESS; } +/* + * Enable/disable buddy's dialog event monitoring. + */ + PJ_DEF(pj_status_t) pjsua_buddy_subscribe_dlg_event(pjsua_buddy_id buddy_id, pj_bool_t subscribe) { diff --git a/pjsip/src/pjsua2/endpoint.cpp b/pjsip/src/pjsua2/endpoint.cpp index cdfbb0cf73..b582554d4d 100644 --- a/pjsip/src/pjsua2/endpoint.cpp +++ b/pjsip/src/pjsua2/endpoint.cpp @@ -1132,6 +1132,18 @@ void Endpoint::on_buddy_state(pjsua_buddy_id buddy_id) buddy->onBuddyState(); } +void Endpoint::on_buddy_dlg_event_state(pjsua_buddy_id buddy_id) +{ + Buddy b(buddy_id); + Buddy *buddy = b.getOriginalInstance(); + if (!buddy || !buddy->isValid()) { + /* Ignored */ + return; + } + + buddy->onBuddyDlgEventState(); +} + void Endpoint::on_buddy_evsub_state(pjsua_buddy_id buddy_id, pjsip_evsub *sub, pjsip_event *event) @@ -1151,6 +1163,25 @@ void Endpoint::on_buddy_evsub_state(pjsua_buddy_id buddy_id, buddy->onBuddyEvSubState(prm); } +void Endpoint::on_buddy_evsub_dlg_event_state(pjsua_buddy_id buddy_id, + pjsip_evsub *sub, + pjsip_event *event) +{ + PJ_UNUSED_ARG(sub); + + Buddy b(buddy_id); + Buddy *buddy = b.getOriginalInstance(); + if (!buddy || !buddy->isValid()) { + /* Ignored */ + return; + } + + OnBuddyEvSubStateParam prm; + prm.e.fromPj(*event); + + buddy->onBuddyEvSubDlgEventState(prm); +} + // Call callbacks void Endpoint::on_call_state(pjsua_call_id call_id, pjsip_event *e) { diff --git a/pjsip/src/pjsua2/presence.cpp b/pjsip/src/pjsua2/presence.cpp index 7625a44264..f024146180 100644 --- a/pjsip/src/pjsua2/presence.cpp +++ b/pjsip/src/pjsua2/presence.cpp @@ -41,6 +41,7 @@ void BuddyConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error) NODE_READ_STRING ( this_node, uri); NODE_READ_BOOL ( this_node, subscribe); + NODE_READ_BOOL ( this_node, subscribe_dlg_event); } void BuddyConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error) @@ -49,6 +50,7 @@ void BuddyConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error) NODE_WRITE_STRING ( this_node, uri); NODE_WRITE_BOOL ( this_node, subscribe); + NODE_WRITE_BOOL ( this_node, subscribe_dlg_event); } ////////////////////////////////////////////////////////////////////////////// @@ -142,6 +144,7 @@ void Buddy::create(Account &account, const BuddyConfig &cfg) pj_cfg.uri = str2Pj(cfg.uri); pj_cfg.subscribe = cfg.subscribe; + pj_cfg.subscribe_dlg_event = cfg.subscribe_dlg_event; pj_cfg.user_data = (void*)bud; pj_cfg.acc_id = account.getId(); PJSUA2_CHECK_EXPR( pjsua_buddy_add(&pj_cfg, &id) ); @@ -183,7 +186,14 @@ void Buddy::subscribePresence(bool subscribe) PJSUA2_THROW(Error) PJSUA2_CHECK_EXPR( pjsua_buddy_subscribe_pres(id, subscribe) ); } - +/* + * Enable/disable buddy's dialog event monitoring. + */ +void Buddy::subscribeDlgEvent(bool subscribe) PJSUA2_THROW(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_buddy_subscribe_dlg_event(id, subscribe) ); +} + /* * Update the presence information for the buddy. */ @@ -191,7 +201,15 @@ void Buddy::updatePresence(void) PJSUA2_THROW(Error) { PJSUA2_CHECK_EXPR( pjsua_buddy_update_pres(id) ); } - + +/* + * Update the dialog event information for the buddy. + */ +void Buddy::updateDlgEvent(void) PJSUA2_THROW(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_buddy_update_dlg_event(id) ); +} + /* * Send instant messaging outside dialog. */ From 3ccc739bf8bb66ab4392861dea093fb2b50da0ed Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 4 Mar 2025 03:14:05 +0100 Subject: [PATCH 212/491] Fix crash due to accessing deleted authentication session parent (#4331) * Fix crash that occurs when the app comes to the foreground and the IP connections were closed before, for example because the power saving mode was activated on the iPhone. The reason was that the client authentication sessions of the regc object still pointed to a parent client authentication session that had already been deleted from the account. * review remarks using auth_clt_set_parent with a NULL parameter removes the parent binding removed auth_session pointer that is not needed anymore --- pjsip/src/pjsip-ua/sip_reg.c | 4 +++- pjsip/src/pjsip/sip_auth_client.c | 10 +++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c index 7595df3ca6..18209b9257 100644 --- a/pjsip/src/pjsip-ua/sip_reg.c +++ b/pjsip/src/pjsip-ua/sip_reg.c @@ -96,7 +96,6 @@ struct pjsip_regc /* Authorization sessions. */ pjsip_auth_clt_sess auth_sess; - pjsip_auth_clt_sess *ext_auth_sess; /**< User defined auth session. */ /* Auto refresh registration. */ pj_bool_t auto_reg; @@ -1222,6 +1221,9 @@ static void regc_tsx_callback(void *token, pjsip_event *event) } } + if (regc->_delete_flag != 0) { + pjsip_auth_clt_set_parent(®c->auth_sess, NULL); + } status = pjsip_auth_clt_reinit_req( ®c->auth_sess, rdata, tsx->last_tx, diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c index f50c489a62..d827a6f177 100644 --- a/pjsip/src/pjsip/sip_auth_client.c +++ b/pjsip/src/pjsip/sip_auth_client.c @@ -838,8 +838,8 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_init( pjsip_auth_clt_sess *sess, PJ_DEF(pj_status_t) pjsip_auth_clt_set_parent(pjsip_auth_clt_sess *sess, pjsip_auth_clt_sess *parent) { - PJ_ASSERT_RETURN(sess && parent, PJ_EINVAL); - if (parent->lock == NULL) { + PJ_ASSERT_RETURN(sess, PJ_EINVAL); + if (parent != NULL && parent->lock == NULL) { pj_status_t status; status = pj_lock_create_simple_mutex( parent->pool, "auth_clt_parent_lock", @@ -907,17 +907,17 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_clone( pj_pool_t *pool, } } - if (sess->parent) { + if (rhs->parent) { pj_status_t status; - pj_lock_acquire(sess->parent->lock); + pj_lock_acquire(rhs->parent->lock); sess->parent = PJ_POOL_ZALLOC_T(pool, pjsip_auth_clt_sess); if (sess->parent == NULL) { status = PJ_ENOMEM; } else { status = pjsip_auth_clt_clone(pool, sess->parent, rhs->parent); } - pj_lock_release(sess->parent->lock); + pj_lock_release(rhs->parent->lock); if (status != PJ_SUCCESS) return status; From 6dabbd5768dce7b2f5f68787e33afa6f701b6634 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 4 Mar 2025 14:39:15 +0700 Subject: [PATCH 213/491] Add dedicated thread method for video sending rate control (#4332) --- pjmedia/include/pjmedia/vid_stream.h | 14 +- pjmedia/src/pjmedia/vid_stream.c | 401 +++++++++++++++++++++++++-- 2 files changed, 388 insertions(+), 27 deletions(-) diff --git a/pjmedia/include/pjmedia/vid_stream.h b/pjmedia/include/pjmedia/vid_stream.h index c1dc5cb50c..f50d3aef9f 100644 --- a/pjmedia/include/pjmedia/vid_stream.h +++ b/pjmedia/include/pjmedia/vid_stream.h @@ -91,7 +91,17 @@ typedef enum pjmedia_vid_stream_rc_method * invoking the video stream put_frame(), e.g: video capture device thread, * will be blocked whenever transmission delay takes place. */ - PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING = 1 + PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING = 1, + + /** + * Using a dedicated sending thread. Outgoing RTP packets will be queued + * to be sent by a dedicated sending thread to avoid peak bandwidth that + * is much higher than specified. Unlike simple blocking, the thread + * invoking the video stream put_frame() will not be blocked. This will + * generally provide better video latency than the simple blocking method + * because of more accurate bitrate calculation. + */ + PJMEDIA_VID_STREAM_RC_SEND_THREAD = 2 } pjmedia_vid_stream_rc_method; @@ -104,7 +114,7 @@ typedef struct pjmedia_vid_stream_rc_config /** * Rate control method. * - * Default: PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING. + * Default: PJMEDIA_VID_STREAM_RC_SEND_THREAD. */ pjmedia_vid_stream_rc_method method; diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c index f36edadd56..dad4462e47 100644 --- a/pjmedia/src/pjmedia/vid_stream.c +++ b/pjmedia/src/pjmedia/vid_stream.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,12 @@ */ typedef pjmedia_channel pjmedia_vid_channel; +/* + * Forward declaration of sending thread structures. + */ +struct send_stream; +struct send_manager; + /** * This structure describes media stream. * A media stream is bidirectional media transmission between two endpoints. @@ -83,7 +90,7 @@ typedef pjmedia_channel pjmedia_vid_channel; */ struct pjmedia_vid_stream { - pjmedia_stream_common base; + pjmedia_stream_common base; pjmedia_vid_codec_mgr *codec_mgr; /**< Codec manager. */ pjmedia_vid_stream_info info; /**< Stream info. */ @@ -133,6 +140,8 @@ struct pjmedia_vid_stream pj_timestamp tx_start; pj_timestamp tx_end; #endif + + struct send_stream *send_stream; }; /* Prototypes */ @@ -148,6 +157,286 @@ static void on_destroy(void *arg); #include "stream_imp_common.c" + +/* + * RTP send scheduler for rate control. + */ + +/* Sending entry */ +typedef struct send_entry +{ + PJ_DECL_LIST_MEMBER(struct send_entry); + + struct send_stream *stream; + void *buf; + pj_size_t buf_size; + + pj_timestamp send_ts; + pj_timestamp sent_ts; +} send_entry; + +/* Sending stream */ +typedef struct send_stream +{ + struct send_manager *mgr; + send_entry free_list; + pj_grp_lock_t *grp_lock; + pjmedia_transport *tp; + pj_pool_t *pool; + pj_size_t buf_size; + const char *name; + + pj_timestamp rc_start; + pj_size_t rc_total; + unsigned rc_bandwidth; + pj_timestamp ts_freq; + unsigned rtp_tx_err_cnt; + +} send_stream; + +/* Sending manager (may be shared by multiple streams in the future) */ +typedef struct send_manager +{ + pj_pool_t *pool; + pj_thread_t *thread; + pj_grp_lock_t *grp_lock; + pj_bool_t is_quitting; + send_entry send_list; +} send_manager; + +/* Sending worker thread. */ +static int send_worker_thread(void* arg) +{ + send_manager* mgr = (send_manager*)arg; + pj_status_t status; + + while (!mgr->is_quitting) { + send_entry *e; + send_stream *s; + pj_timestamp now; + + /* Find a packet to send */ + pj_grp_lock_acquire(mgr->grp_lock); + pj_get_timestamp(&now); + e = mgr->send_list.next; + while (e != &mgr->send_list) { + if (pj_cmp_timestamp(&now, &e->send_ts) >= 0) { + pj_list_erase(e); + break; + } + + e = e->next; + } + pj_grp_lock_release(mgr->grp_lock); + + /* No packet to send, just sleep */ + if (e == &mgr->send_list) { + pj_thread_sleep(10); + continue; + } + + /* Send the packet */ + s = e->stream; + status = pjmedia_transport_send_rtp(s->tp, e->buf, e->buf_size); + if (status != PJ_SUCCESS) { + if (s->rtp_tx_err_cnt++ == 0) + LOGERR_((s->name, status, "Error sending RTP")); + else if (s->rtp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) + s->rtp_tx_err_cnt = 0; + } + + pj_grp_lock_acquire(s->grp_lock); + + /* Update or reset total sent */ + if (status == PJ_SUCCESS) { + s->rc_total += e->buf_size; + if (s->rc_total > 0xFF000000) { + s->rc_total = 0; + s->rc_start = now; + } + } + + /* Put back send buffer to free list */ + e->sent_ts = now; + pj_list_push_back(&s->free_list, e); + + pj_grp_lock_release(s->grp_lock); + + /* Dec ref the stream (ref added in queuing packet) */ + pj_grp_lock_dec_ref(s->grp_lock); + + } + + return 0; +} + +/* Group lock destructor of send manager */ +static void send_mgr_on_destroy(void *arg) +{ + send_manager* mgr = (send_manager*)arg; + + if (mgr->thread) { + mgr->is_quitting = PJ_TRUE; + pj_thread_join(mgr->thread); + pj_thread_destroy(mgr->thread); + } + + if (mgr->pool) + pj_pool_safe_release(&mgr->pool); +} + +/* Attach send manager to stream. + * This may also initialize the specified send manager. + */ +static pj_status_t attach_send_manager(send_stream *ss, send_manager *mgr) +{ + pj_status_t status = PJ_SUCCESS; + + /* Initialize manager if not yet */ + if (!mgr->pool) { + pj_pool_t *pool; + + /* Create pool */ + pool = pj_pool_create(ss->pool->factory, "stream_send_mgr", + 1024, 1024, NULL); + if (!pool) + return PJ_ENOMEM; + + mgr->pool = pool; + mgr->is_quitting = PJ_FALSE; + pj_list_init(&mgr->send_list); + + /* Create group lock */ + status = pj_grp_lock_create_w_handler(pool, NULL, mgr, + &send_mgr_on_destroy, + &mgr->grp_lock); + if (status != PJ_SUCCESS) + goto on_return; + + /* Create thread */ + status = pj_thread_create(pool, "send_mgr", &send_worker_thread, mgr, + 0, 0, &mgr->thread); + if (status != PJ_SUCCESS) + goto on_return; + } + +on_return: + if (status != PJ_SUCCESS) { + if (mgr->grp_lock) { + /* This should also invoke send_mgr_on_destroy() */ + pj_grp_lock_destroy(mgr->grp_lock); + } + return status; + } + + ss->mgr = mgr; + + /* Add ref counter */ + return pj_grp_lock_add_ref(mgr->grp_lock); +} + +/* Detach send manager from stream. + * This may destroy the send manager when no stream is using it. + */ +static pj_status_t detach_send_manager(send_stream *ss) +{ + send_manager *mgr = ss->mgr; + send_entry *e; + + /* Remove all packets of this stream from queue */ + pj_grp_lock_acquire(mgr->grp_lock); + e = mgr->send_list.next; + while (e != &mgr->send_list) { + send_entry *next = e->next; + if (e->stream == ss) { + send_stream *s = e->stream; + + pj_list_erase(e); + + /* Dec ref the stream (ref added in queuing packet) */ + pj_grp_lock_dec_ref(s->grp_lock); + + //pj_grp_lock_release(s->grp_lock); + //pj_list_push_back(&s->free_list, e); + //pj_grp_lock_release(s->grp_lock); + } + e = next; + } + pj_grp_lock_release(mgr->grp_lock); + + /* Decrease ref counter */ + return pj_grp_lock_dec_ref(mgr->grp_lock); +} + +/* Allocate buffer for sending packet */ +static send_entry* get_send_entry(send_stream *ss) +{ + send_entry *e; + pj_timestamp min_sent_ts, idle_ts; + + /* Find entry which has idled for at least 1/4 seconds */ + idle_ts.u64 = ss->ts_freq.u64 >> 2; + + pj_get_timestamp(&min_sent_ts); + pj_sub_timestamp(&min_sent_ts, &idle_ts); + + pj_grp_lock_acquire(ss->grp_lock); + e = ss->free_list.next; + while (e != &ss->free_list) { + send_entry *next = e->next; + if (pj_cmp_timestamp(&min_sent_ts, &e->sent_ts) >= 0) { + pj_list_erase(e); + break; + } + e = next; + } + pj_grp_lock_release(ss->grp_lock); + + if (e != &ss->free_list) + return e; + + e = PJ_POOL_ZALLOC_T(ss->pool, send_entry); + if (!e) + return NULL; + + e->buf = pj_pool_alloc(ss->pool, ss->buf_size); + if (!e->buf) + return NULL; + + e->buf_size = ss->buf_size; + e->stream = ss; + return e; +} + +/* Schedule sending an RTP packet to send manager */ +static void send_rtp(send_stream *ss, send_entry *entry) +{ + pj_timestamp send_ts; + + /* Calculate earliest sending time allowed by rate control */ + pj_grp_lock_acquire(ss->grp_lock); + if (ss->rc_start.u64 == 0) + pj_get_timestamp(&ss->rc_start); + entry->send_ts = ss->rc_start; + send_ts.u64 = (ss->rc_total + entry->buf_size) * ss->ts_freq.u64 * 8 / + ss->rc_bandwidth; + pj_grp_lock_release(ss->grp_lock); + pj_add_timestamp(&entry->send_ts, &send_ts); + + /* Queue the packet */ + pj_grp_lock_acquire(ss->mgr->grp_lock); + pj_list_push_back(&ss->mgr->send_list, entry); + pj_grp_lock_release(ss->mgr->grp_lock); + + /* Add ref stream to avoid premature destroy of stream */ + pj_grp_lock_add_ref(ss->grp_lock); +} + + +/* + * Internal functions. + */ + static void dump_port_info(const pjmedia_vid_channel *chan, const char *event_name) { @@ -434,6 +723,8 @@ static pj_status_t put_frame(pjmedia_port *port, pj_timestamp initial_time; pj_timestamp now; pj_timestamp null_ts ={{0}}; + send_entry *send_entry = NULL; + void *send_buf; #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0 /* If the interval since last sending packet is greater than @@ -489,7 +780,18 @@ static pj_status_t put_frame(pjmedia_port *port, /* Init frame_out buffer. */ pj_bzero(&frame_out, sizeof(frame_out)); - frame_out.buf = ((char*)channel->buf) + sizeof(pjmedia_rtp_hdr); + if (stream->info.rc_cfg.method == PJMEDIA_VID_STREAM_RC_SEND_THREAD) { + send_entry = get_send_entry(stream->send_stream); + if (!send_entry) { + LOGERR_((channel->port.info.name.ptr, PJ_ENOMEM, + "Cannot allocate send entry")); + return PJ_ENOMEM; + } + send_buf = send_entry->buf; + } else { + send_buf = channel->buf; + } + frame_out.buf = ((char*)send_buf) + sizeof(pjmedia_rtp_hdr); /* Check if need to send keyframe. */ pj_get_timestamp(&now); @@ -565,20 +867,26 @@ static pj_status_t put_frame(pjmedia_port *port, */ if (frame_out.size != 0 && c_strm->transport) { /* Copy RTP header to the beginning of packet */ - pj_memcpy(channel->buf, rtphdr, sizeof(pjmedia_rtp_hdr)); + pj_memcpy(send_buf, rtphdr, sizeof(pjmedia_rtp_hdr)); /* Send the RTP packet to the transport. */ - status = pjmedia_transport_send_rtp(c_strm->transport, - (char*)channel->buf, - frame_out.size + - sizeof(pjmedia_rtp_hdr)); - if (status != PJ_SUCCESS) { - if (c_strm->rtp_tx_err_cnt++ == 0) { - LOGERR_((channel->port.info.name.ptr, status, - "Error sending RTP")); - } - if (c_strm->rtp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) { - c_strm->rtp_tx_err_cnt = 0; + if (stream->info.rc_cfg.method==PJMEDIA_VID_STREAM_RC_SEND_THREAD) + { + send_entry->buf_size = frame_out.size+sizeof(pjmedia_rtp_hdr); + send_rtp(stream->send_stream, send_entry); + } else { + status = pjmedia_transport_send_rtp(c_strm->transport, + (char*)send_buf, + frame_out.size + + sizeof(pjmedia_rtp_hdr)); + if (status != PJ_SUCCESS) { + if (c_strm->rtp_tx_err_cnt++ == 0) { + LOGERR_((channel->port.info.name.ptr, status, + "Error sending RTP")); + } else + if (c_strm->rtp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) { + c_strm->rtp_tx_err_cnt = 0; + } } } pjmedia_rtcp_tx_rtp(&c_strm->rtcp, (unsigned)frame_out.size); @@ -592,7 +900,20 @@ static pj_status_t put_frame(pjmedia_port *port, /* Next packets use same timestamp */ rtp_ts_len = 0; + /* Update frame_out buffer. */ frame_out.size = 0; + if (stream->info.rc_cfg.method == PJMEDIA_VID_STREAM_RC_SEND_THREAD) { + send_entry = get_send_entry(stream->send_stream); + if (!send_entry) { + LOGERR_((channel->port.info.name.ptr, PJ_ENOMEM, + "Cannot allocate send entry")); + return PJ_ENOMEM; + } + send_buf = send_entry->buf; + } else { + send_buf = channel->buf; + } + frame_out.buf = ((char*)send_buf) + sizeof(pjmedia_rtp_hdr); /* Encode more! */ status = pjmedia_vid_codec_encode_more(stream->codec, @@ -1379,6 +1700,16 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( &c_strm->rtcp); } + /* Save the stream info */ + pj_memcpy(&stream->info, info, sizeof(*info)); + c_strm->si = (pjmedia_stream_info_common *)&stream->info; + stream->info.codec_param = pjmedia_vid_codec_param_clone( + pool, info->codec_param); + pjmedia_rtcp_fb_info_dup(pool, &stream->info.loc_rtcp_fb, + &info->loc_rtcp_fb); + pjmedia_rtcp_fb_info_dup(pool, &stream->info.rem_rtcp_fb, + &info->rem_rtcp_fb); + /* Allocate outgoing RTCP buffer, should be enough to hold SR/RR, SDES, * BYE, Feedback, and XR. */ @@ -1460,16 +1791,6 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( } #endif - /* Save the stream info */ - pj_memcpy(&stream->info, info, sizeof(*info)); - c_strm->si = (pjmedia_stream_info_common *)&stream->info; - stream->info.codec_param = pjmedia_vid_codec_param_clone( - pool, info->codec_param); - pjmedia_rtcp_fb_info_dup(pool, &stream->info.loc_rtcp_fb, - &info->loc_rtcp_fb); - pjmedia_rtcp_fb_info_dup(pool, &stream->info.rem_rtcp_fb, - &info->rem_rtcp_fb); - /* Check if we should send RTCP-FB */ if (stream->info.rem_rtcp_fb.cap_count) { pjmedia_rtcp_fb_info *rfi = &stream->info.rem_rtcp_fb; @@ -1509,6 +1830,32 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( } } + /* Initialize send manager for send rate control */ + if (stream->info.rc_cfg.method == PJMEDIA_VID_STREAM_RC_SEND_THREAD) { + send_manager *send_mgr; + send_stream *ss; + + send_mgr = PJ_POOL_ZALLOC_T(c_strm->own_pool, send_manager); + ss = PJ_POOL_ZALLOC_T(c_strm->own_pool, send_stream); + if (!send_mgr || !ss) + goto err_cleanup; + + pj_list_init(&ss->free_list); + ss->grp_lock = c_strm->grp_lock; + ss->tp = c_strm->transport; + ss->pool = c_strm->own_pool; + ss->buf_size = c_strm->enc->buf_size; + ss->ts_freq = stream->ts_freq; + ss->rc_bandwidth = stream->info.rc_cfg.bandwidth; + ss->name = c_strm->enc->port.info.name.ptr; + + status = attach_send_manager(ss, send_mgr); + if (status != PJ_SUCCESS) + goto err_cleanup; + + stream->send_stream = ss; + } + /* Success! */ *p_stream = stream; @@ -1540,6 +1887,10 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy( pjmedia_vid_stream *stream ) if (c_strm->dec) c_strm->dec->port.get_frame = NULL; + /* Detach from sending manager */ + if (stream->send_stream) + detach_send_manager(stream->send_stream); + #if TRACE_RC { unsigned total_time; @@ -1881,7 +2232,7 @@ PJ_DEF(void) pjmedia_vid_stream_rc_config_default(pjmedia_vid_stream_rc_config *cfg) { pj_bzero(cfg, sizeof(*cfg)); - cfg->method = PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING; + cfg->method = PJMEDIA_VID_STREAM_RC_SEND_THREAD; } From 69b7974f6b1aa2007e2d8b7b715307ad7c79ea4e Mon Sep 17 00:00:00 2001 From: LeonidGoltsblat <138720759+LeonidGoltsblat@users.noreply.github.com> Date: Thu, 6 Mar 2025 06:19:10 +0300 Subject: [PATCH 214/491] fis compiler warning on ut_app_init1() declaration (#4339) warning C4459: declaration of 'mem' hides global declaration --- pjlib/src/pjlib-test/test_util.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pjlib/src/pjlib-test/test_util.h b/pjlib/src/pjlib-test/test_util.h index a6714718fc..fc0fd5c533 100644 --- a/pjlib/src/pjlib-test/test_util.h +++ b/pjlib/src/pjlib-test/test_util.h @@ -63,9 +63,9 @@ PJ_INLINE(void) ut_app_init0(ut_app_t *ut_app) } /* Call this in test.c before adding test cases */ -PJ_INLINE(pj_status_t) ut_app_init1(ut_app_t *ut_app, pj_pool_factory *mem) +PJ_INLINE(pj_status_t) ut_app_init1(ut_app_t *ut_app, pj_pool_factory *mem_) { - ut_app->pool = pj_pool_create(mem, THIS_FILE, 4000, 4000, NULL); + ut_app->pool = pj_pool_create(mem_, THIS_FILE, 4000, 4000, NULL); PJ_TEST_NOT_NULL(ut_app->pool, NULL, return PJ_ENOMEM); pj_test_suite_init(&ut_app->suite); return PJ_SUCCESS; From cc538f9fff29ba66c8e74084149d20688a6eb096 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 6 Mar 2025 10:23:33 +0700 Subject: [PATCH 215/491] Cancel timer of sound device idle check (#4340) --- pjsip/src/pjsua-lib/pjsua_aud.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index 24003b4f18..689e0cf6fd 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -2155,6 +2155,12 @@ static void close_snd_dev(void) { pj_log_push_indent(); + /* Cancel sound device idle timer. */ + if (pjsua_var.snd_idle_timer.id) { + pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.snd_idle_timer); + pjsua_var.snd_idle_timer.id = PJ_FALSE; + } + /* Notify app */ if (pjsua_var.snd_is_on && pjsua_var.ua_cfg.cb.on_snd_dev_operation) { (*pjsua_var.ua_cfg.cb.on_snd_dev_operation)(0); From b891b2833d3afd4ad3cd3f5ce3d8c62193a28e66 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 6 Mar 2025 14:29:39 +0700 Subject: [PATCH 216/491] Update sending rate control using thread (#4341) To fix & minor enhance #4332: - Fix sending time calculation, the total sent should be updated in each queuing packet. - Protect stream pool usage using group lock. - Minor optimization: faster buffer reuse to avoid too many buffers, skip printing trace log for sending RTP when there is no packet to send. --- pjmedia/src/pjmedia/vid_stream.c | 78 ++++++++++++++++---------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c index dad4462e47..f8479f5c59 100644 --- a/pjmedia/src/pjmedia/vid_stream.c +++ b/pjmedia/src/pjmedia/vid_stream.c @@ -172,7 +172,6 @@ typedef struct send_entry pj_size_t buf_size; pj_timestamp send_ts; - pj_timestamp sent_ts; } send_entry; /* Sending stream */ @@ -192,6 +191,9 @@ typedef struct send_stream pj_timestamp ts_freq; unsigned rtp_tx_err_cnt; +#if TRACE_RC + pj_size_t entry_cnt; +#endif } send_stream; /* Sending manager (may be shared by multiple streams in the future) */ @@ -236,6 +238,7 @@ static int send_worker_thread(void* arg) } /* Send the packet */ + e->send_ts = now; s = e->stream; status = pjmedia_transport_send_rtp(s->tp, e->buf, e->buf_size); if (status != PJ_SUCCESS) { @@ -245,21 +248,9 @@ static int send_worker_thread(void* arg) s->rtp_tx_err_cnt = 0; } - pj_grp_lock_acquire(s->grp_lock); - - /* Update or reset total sent */ - if (status == PJ_SUCCESS) { - s->rc_total += e->buf_size; - if (s->rc_total > 0xFF000000) { - s->rc_total = 0; - s->rc_start = now; - } - } - /* Put back send buffer to free list */ - e->sent_ts = now; + pj_grp_lock_acquire(s->grp_lock); pj_list_push_back(&s->free_list, e); - pj_grp_lock_release(s->grp_lock); /* Dec ref the stream (ref added in queuing packet) */ @@ -372,10 +363,11 @@ static pj_status_t detach_send_manager(send_stream *ss) static send_entry* get_send_entry(send_stream *ss) { send_entry *e; + void *buf; pj_timestamp min_sent_ts, idle_ts; - /* Find entry which has idled for at least 1/4 seconds */ - idle_ts.u64 = ss->ts_freq.u64 >> 2; + /* Find entry which has idled for at least 1/16 seconds */ + idle_ts.u64 = ss->ts_freq.u64 >> 4; pj_get_timestamp(&min_sent_ts); pj_sub_timestamp(&min_sent_ts, &idle_ts); @@ -384,27 +376,29 @@ static send_entry* get_send_entry(send_stream *ss) e = ss->free_list.next; while (e != &ss->free_list) { send_entry *next = e->next; - if (pj_cmp_timestamp(&min_sent_ts, &e->sent_ts) >= 0) { + if (pj_cmp_timestamp(&min_sent_ts, &e->send_ts) >= 0) { pj_list_erase(e); break; } e = next; } - pj_grp_lock_release(ss->grp_lock); - - if (e != &ss->free_list) - return e; - - e = PJ_POOL_ZALLOC_T(ss->pool, send_entry); - if (!e) - return NULL; - e->buf = pj_pool_alloc(ss->pool, ss->buf_size); - if (!e->buf) - return NULL; + if (e == &ss->free_list) { + /* Not found, allocate a new one */ + e = PJ_POOL_ZALLOC_T(ss->pool, send_entry); + buf = pj_pool_alloc(ss->pool, ss->buf_size); + if (!e || !buf) + return NULL; - e->buf_size = ss->buf_size; - e->stream = ss; + e->buf = buf; + e->buf_size = ss->buf_size; + e->stream = ss; +#if TRACE_RC + ss->entry_cnt++; + PJ_LOG(5, (ss->name, "Send entry count=%d", ss->entry_cnt)); +#endif + } + pj_grp_lock_release(ss->grp_lock); return e; } @@ -413,23 +407,31 @@ static void send_rtp(send_stream *ss, send_entry *entry) { pj_timestamp send_ts; - /* Calculate earliest sending time allowed by rate control */ pj_grp_lock_acquire(ss->grp_lock); + + /* Calculate earliest sending time allowed by rate control */ + ss->rc_total += entry->buf_size; + send_ts.u64 = ss->rc_total * ss->ts_freq.u64 * 8 / ss->rc_bandwidth; if (ss->rc_start.u64 == 0) pj_get_timestamp(&ss->rc_start); entry->send_ts = ss->rc_start; - send_ts.u64 = (ss->rc_total + entry->buf_size) * ss->ts_freq.u64 * 8 / - ss->rc_bandwidth; - pj_grp_lock_release(ss->grp_lock); pj_add_timestamp(&entry->send_ts, &send_ts); + /* Reset counter to avoid overflow in calculating timestamp */ + if (ss->rc_total > 0xFF000000) { + ss->rc_total = 0; + ss->rc_start = entry->send_ts; + } + + /* Add ref stream to avoid premature destroy of stream */ + pj_grp_lock_add_ref(ss->grp_lock); + + pj_grp_lock_release(ss->grp_lock); + /* Queue the packet */ pj_grp_lock_acquire(ss->mgr->grp_lock); pj_list_push_back(&ss->mgr->send_list, entry); pj_grp_lock_release(ss->mgr->grp_lock); - - /* Add ref stream to avoid premature destroy of stream */ - pj_grp_lock_add_ref(ss->grp_lock); } @@ -953,7 +955,7 @@ static pj_status_t put_frame(pjmedia_port *port, #if TRACE_RC /* Trace log for rate control */ - { + if (pkt_cnt) { pj_timestamp end_time; unsigned total_sleep; From 23b43985d0d63481010d6b73c818c5e9b5c80c54 Mon Sep 17 00:00:00 2001 From: thanhloi2603 <42855486+thanhloi2603@users.noreply.github.com> Date: Fri, 7 Mar 2025 05:42:54 +0700 Subject: [PATCH 217/491] Add declaration for buddy dlg events in libInit (#4342) --- pjsip/src/pjsua2/endpoint.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pjsip/src/pjsua2/endpoint.cpp b/pjsip/src/pjsua2/endpoint.cpp index b582554d4d..33495d0213 100644 --- a/pjsip/src/pjsua2/endpoint.cpp +++ b/pjsip/src/pjsua2/endpoint.cpp @@ -1988,6 +1988,8 @@ void Endpoint::libInit(const EpConfig &prmEpConfig) PJSUA2_THROW(Error) ua_cfg.cb.on_mwi_info = &Endpoint::on_mwi_info; ua_cfg.cb.on_buddy_state = &Endpoint::on_buddy_state; ua_cfg.cb.on_buddy_evsub_state = &Endpoint::on_buddy_evsub_state; + ua_cfg.cb.on_buddy_dlg_event_state = &Endpoint::on_buddy_dlg_event_state; + ua_cfg.cb.on_buddy_evsub_dlg_event_state = &Endpoint::on_buddy_evsub_dlg_event_state; ua_cfg.cb.on_acc_find_for_incoming = &Endpoint::on_acc_find_for_incoming; ua_cfg.cb.on_ip_change_progress = &Endpoint::on_ip_change_progress; From cd96f03d1e1338ca11a0c6f699ac1839fe6acfcf Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Mon, 10 Mar 2025 12:27:19 +0700 Subject: [PATCH 218/491] Delay video encoding start to suppress early RTP packet lost (#4343) --- pjsip/include/pjsua-lib/pjsua.h | 15 +++++++++++ pjsip/src/pjsua-lib/pjsua_vid.c | 46 ++++++++++++++++++++++++++++----- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 7c4092e102..3ee85bec84 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -7323,6 +7323,21 @@ PJ_DECL(pj_status_t) pjsua_im_typing(pjsua_acc_id acc_id, #endif +/** + * Specify the delay of video stream start in encoding direction, in + * millisecond. Delayed encoding start generally smoothens video stream + * initiation by reducing the risk of RTP packet loss in the receiver/decoder + * (e.g.: due to the receiver's media transport or stream being unready). + * Note that initial video RTP packets usually contain crucial information, + * such as video codec parameters and keyframes. + * + * Default: 500ms + */ +#ifndef PJSUA_VIDEO_STREAM_DELAY_START_ENCODE +# define PJSUA_VIDEO_STREAM_DELAY_START_ENCODE 500 +#endif + + /** * This structure describes media configuration, which will be specified * when calling #pjsua_init(). Application MUST initialize this structure diff --git a/pjsip/src/pjsua-lib/pjsua_vid.c b/pjsip/src/pjsua-lib/pjsua_vid.c index e73badf8cb..6204a29b53 100644 --- a/pjsip/src/pjsua-lib/pjsua_vid.c +++ b/pjsip/src/pjsua-lib/pjsua_vid.c @@ -1065,6 +1065,17 @@ static pj_status_t setup_vid_capture(pjsua_call_media *call_med) return status; } + +static void timer_on_start_vid_stream_encoding(void *user_data) +{ + pjmedia_vid_stream *vid_strm = (pjmedia_vid_stream*)user_data; + pjmedia_stream_common *c_strm = (pjmedia_stream_common*)vid_strm; + + pjmedia_vid_stream_resume(vid_strm, PJMEDIA_DIR_ENCODING); + pj_grp_lock_dec_ref(c_strm->grp_lock); +} + + /* Internal function: update video channel after SDP negotiation. * Warning: do not use temporary/flip-flop pool, e.g: inv->pool_prov, * for creating stream, etc, as after SDP negotiation and when @@ -1194,6 +1205,34 @@ pj_status_t pjsua_vid_channel_update(pjsua_call_media *call_med, if (status != PJ_SUCCESS) goto on_error; + /* Temporarily pause the encoding to avoid early RTP packet lost + * because media transports are being setup/attached to streams + * and CPU may be spiking after SDP nego. + */ + status = pjmedia_vid_stream_pause(call_med->strm.v.stream, + PJMEDIA_DIR_ENCODING); + if (status != PJ_SUCCESS) + goto on_error; + + /* Schedule start stream encoding */ + if (acc->cfg.vid_out_auto_transmit) { + pjmedia_stream_common *c_strm = (pjmedia_stream_common*) + call_med->strm.v.stream; + pj_grp_lock_add_ref(c_strm->grp_lock); + if (pjsua_schedule_timer2(&timer_on_start_vid_stream_encoding, + call_med->strm.v.stream, + PJSUA_VIDEO_STREAM_DELAY_START_ENCODE) + != PJ_SUCCESS) + { + if (PJSUA_VIDEO_STREAM_DELAY_START_ENCODE) { + PJ_LOG(4,(THIS_FILE, "Failed in scheduling video stream " + "encoding start, start it now.")); + } + /* Just in case there is failure in scheduling */ + timer_on_start_vid_stream_encoding(call_med->strm.v.stream); + } + } + if (call_med->prev_state == PJSUA_CALL_MEDIA_NONE) pjmedia_vid_stream_send_rtcp_sdes(call_med->strm.v.stream); @@ -1310,13 +1349,6 @@ pj_status_t pjsua_vid_channel_update(pjsua_call_media *call_med, } } - if (!acc->cfg.vid_out_auto_transmit && call_med->strm.v.stream) { - status = pjmedia_vid_stream_pause(call_med->strm.v.stream, - PJMEDIA_DIR_ENCODING); - if (status != PJ_SUCCESS) - goto on_error; - } - pj_log_pop_indent(); return PJ_SUCCESS; From b1bb6e37b77c1a287ea678742d43b2e03d95f098 Mon Sep 17 00:00:00 2001 From: Yang sheng Date: Wed, 12 Mar 2025 12:45:38 +0800 Subject: [PATCH 219/491] Fix PJ_PERROR message mismatch when call pjsua_vid_channel_update (#4349) --- pjsip/src/pjsua-lib/pjsua_media.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index ce988d26df..43e3960ecf 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -4045,9 +4045,10 @@ static pj_status_t apply_med_update(pjsua_call_media *call_med, if (call->opt.flag & PJSUA_CALL_SET_MEDIA_DIR) { call_med->def_dir = call->opt.media_dir[mi]; - PJ_LOG(4,(THIS_FILE, "Call %d: setting audio media " + PJ_LOG(4,(THIS_FILE, "Call %d: setting %s media " "direction #%d to %d.", - call_id, mi, call_med->def_dir)); + call_id, pjmedia_type_name(call_med->type), mi, + call_med->def_dir)); } /* If the default direction specifies we do not wish @@ -4189,7 +4190,7 @@ static pj_status_t apply_med_update(pjsua_call_media *call_med, if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "pjmedia_transport_media_start() failed " - "for call_id %d media %d", + "for call_id %d media %d", call_id, mi)); return status; } @@ -4221,8 +4222,9 @@ static pj_status_t apply_med_update(pjsua_call_media *call_med, } if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, - "pjsua_aud_channel_update() failed " - "for call_id %d media %d", + "%s channel_update failed " + "for call_id %d media %d", + pjmedia_type_name(call_med->type), call_id, mi)); return status; } From 0726c5641b9f0ddd6daad5217291e0fd507bf233 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Fri, 14 Mar 2025 15:33:44 +0700 Subject: [PATCH 220/491] Fix IOCP: key destroy handler not called when app supplies group lock (#4355) --- pjlib/src/pj/ioqueue_winnt.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/pjlib/src/pj/ioqueue_winnt.c b/pjlib/src/pj/ioqueue_winnt.c index b7de3ffe65..676493a050 100644 --- a/pjlib/src/pj/ioqueue_winnt.c +++ b/pjlib/src/pj/ioqueue_winnt.c @@ -603,6 +603,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool, pj_ioqueue_key_t *rec; u_long value; int rc; + pj_status_t status; PJ_ASSERT_RETURN(pool && ioqueue && cb && key, PJ_EINVAL); @@ -642,9 +643,9 @@ PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool, pj_memcpy(&rec->cb, cb, sizeof(pj_ioqueue_callback)); /* Set concurrency for this handle */ - rc = pj_ioqueue_set_concurrency(rec, ioqueue->default_concurrency); - if (rc != PJ_SUCCESS) - return rc; + status = pj_ioqueue_set_concurrency(rec, ioqueue->default_concurrency); + if (status != PJ_SUCCESS) + return status; #if PJ_HAS_TCP rec->connecting = 0; @@ -664,14 +665,17 @@ PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool, /* Create group lock if not specified */ if (!grp_lock) { - pj_status_t status; status = pj_grp_lock_create_w_handler(rec->pool, NULL, rec, &key_on_destroy, &grp_lock); - if (status != PJ_SUCCESS) { - key_on_destroy(rec); - return status; - } + } else { + status = pj_grp_lock_add_handler(grp_lock, rec->pool, rec, + &key_on_destroy); } + if (status != PJ_SUCCESS) { + key_on_destroy(rec); + return status; + } + rec->grp_lock = grp_lock; /* Set initial reference count to 1 */ @@ -801,7 +805,7 @@ static struct pending_op *alloc_pending_op(pj_ioqueue_key_t *key, /* Link app op key to pending-op */ op_key->internal__[PENDING_OP_POS(op_key)] = op; - TRACE((THIS_FILE, "ALLOC op key %p (cnt=%d) op %p", key, ref_cnt, op)); + TRACE((THIS_FILE, "ALLOC op key %p (cnt=%d) op %p", key, ref_cnt-1, op)); return op; } @@ -818,7 +822,7 @@ static void release_pending_op(pj_ioqueue_key_t *key, struct pending_op *op) ref_cnt = pj_grp_lock_get_ref(key->grp_lock); pj_ioqueue_unlock_key(key); - TRACE((THIS_FILE, "RELEASE op key %p (cnt=%d) op %p", key, ref_cnt, op)); + TRACE((THIS_FILE, "RELEASE op key %p (cnt=%d) op %p", key, ref_cnt-1, op)); } static pj_status_t cancel_all_pending_op(pj_ioqueue_key_t *key) From 8daeaa6f045a6cb1bc2a2c9a19c7552293597102 Mon Sep 17 00:00:00 2001 From: Tomasz Motylewski Date: Mon, 17 Mar 2025 03:14:26 +0100 Subject: [PATCH 221/491] fix pj_time_decode for 64 bit time_t and 32 bit long int, (time_t*) casting was wrong (#4357) --- pjlib/src/pj/os_time_common.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pjlib/src/pj/os_time_common.c b/pjlib/src/pj/os_time_common.c index 98f2fc66c5..e910db5c78 100644 --- a/pjlib/src/pj/os_time_common.c +++ b/pjlib/src/pj/os_time_common.c @@ -28,14 +28,15 @@ PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt) { struct tm local_time; + time_t sec = tv->sec; // time_t might be 64 bit even when long int is 32 bit, (time_t*) dangerous PJ_CHECK_STACK(); #if defined(PJ_HAS_LOCALTIME_R) && PJ_HAS_LOCALTIME_R != 0 - localtime_r((time_t*)&tv->sec, &local_time); + localtime_r(&sec, &local_time); #else /* localtime() is NOT thread-safe. */ - local_time = *localtime((time_t*)&tv->sec); + local_time = *localtime(&sec); #endif pt->year = local_time.tm_year+1900; From 76bd3dbf37d7d69ff47dc777f4db9a2a8329bca6 Mon Sep 17 00:00:00 2001 From: PesalaDeSilva Date: Mon, 17 Mar 2025 09:20:53 +0530 Subject: [PATCH 222/491] Avoid 100rel retransmit if invite transaction no longer exists (#4338) --- pjlib/src/pj/os_core_win32.c | 1 + pjmedia/include/pjmedia/circbuf.h | 9 +++++++-- pjsip/src/pjsip-ua/sip_100rel.c | 17 ++++++++++++----- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c index 68a538dcba..37a84d74c7 100644 --- a/pjlib/src/pj/os_core_win32.c +++ b/pjlib/src/pj/os_core_win32.c @@ -981,6 +981,7 @@ PJ_DEF(void*) pj_thread_local_get(long index) //Can't check stack because this function is called //by PJ_CHECK_STACK() itself!!! //PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(index >= 0, NULL); #if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 return TlsGetValueRT(index); #else diff --git a/pjmedia/include/pjmedia/circbuf.h b/pjmedia/include/pjmedia/circbuf.h index d6caabfa24..17a0f39253 100644 --- a/pjmedia/include/pjmedia/circbuf.h +++ b/pjmedia/include/pjmedia/circbuf.h @@ -84,9 +84,14 @@ PJ_INLINE(pj_status_t) pjmedia_circ_buf_create(pj_pool_t *pool, { pjmedia_circ_buf *cbuf; + *p_cb = NULL; + cbuf = PJ_POOL_ZALLOC_T(pool, pjmedia_circ_buf); - cbuf->buf = (pj_int16_t*) pj_pool_calloc(pool, capacity, - sizeof(pj_int16_t)); + if (!cbuf) + return PJ_ENOMEM; + cbuf->buf = (pj_int16_t*) pj_pool_calloc(pool, capacity, sizeof(pj_int16_t)); + if (!cbuf->buf) + return PJ_ENOMEM; cbuf->capacity = capacity; cbuf->start = cbuf->buf; cbuf->len = 0; diff --git a/pjsip/src/pjsip-ua/sip_100rel.c b/pjsip/src/pjsip-ua/sip_100rel.c index d253c8bceb..4e592a1275 100644 --- a/pjsip/src/pjsip-ua/sip_100rel.c +++ b/pjsip/src/pjsip-ua/sip_100rel.c @@ -558,6 +558,7 @@ static void on_retransmit(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { dlg_data *dd; + pjsip_transaction* invite_tsx; tx_data_list_t *tl; pjsip_tx_data *tdata; pj_bool_t final; @@ -569,6 +570,12 @@ static void on_retransmit(pj_timer_heap_t *timer_heap, entry->id = PJ_FALSE; + invite_tsx = dd->inv->invite_tsx; + if (!invite_tsx) { + clear_all_responses(dd); + return; + } + ++dd->uas_state->retransmit_count; if (dd->uas_state->retransmit_count >= 7) { /* If a reliable provisional response is retransmitted for @@ -582,11 +589,11 @@ static void on_retransmit(pj_timer_heap_t *timer_heap, /* Clear all pending responses */ clear_all_responses(dd); - /* Send 500 response */ - status = pjsip_inv_end_session(dd->inv, 500, &reason, &tdata); + /* Send 504 (Server Time-out) response */ + status = pjsip_inv_end_session(dd->inv, 504, &reason, &tdata); if (status == PJ_SUCCESS && tdata) { pjsip_dlg_send_response(dd->inv->dlg, - dd->inv->invite_tsx, + invite_tsx, tdata); } return; @@ -602,14 +609,14 @@ static void on_retransmit(pj_timer_heap_t *timer_heap, if (dd->uas_state->retransmit_count == 1) { pj_status_t status; - status = pjsip_tsx_send_msg(dd->inv->invite_tsx, tdata); + status = pjsip_tsx_send_msg(invite_tsx, tdata); if (status != PJ_SUCCESS) { PJ_PERROR(3, (THIS_FILE, status, "Failed to send message")); return; } } else { - pjsip_tsx_retransmit_no_state(dd->inv->invite_tsx, tdata); + pjsip_tsx_retransmit_no_state(invite_tsx, tdata); } if (final) { From 6ed3c29e47c48114babcd642027b3f6acb323ba7 Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 19 Mar 2025 19:43:55 +0800 Subject: [PATCH 223/491] Fixed pjsip test build failure (#4362) --- pjsip/build/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pjsip/build/Makefile b/pjsip/build/Makefile index 3238446708..c3146b9b2b 100644 --- a/pjsip/build/Makefile +++ b/pjsip/build/Makefile @@ -192,8 +192,8 @@ export TEST_LDFLAGS += $(PJSIP_LDLIB) \ $(PJMEDIA_VIDEODEV_LDLIB) \ $(PJMEDIA_LDLIB) \ $(PJMEDIA_AUDIODEV_LDLIB) \ - $(PJLIB_UTIL_LDLIB) \ $(PJNATH_LDLIB) \ + $(PJLIB_UTIL_LDLIB) \ $(PJLIB_LDLIB) \ $(_LDFLAGS) ifeq ($(EXCLUDE_APP),0) From 4d3a7f1e0f20dc8e7db82ae32b019a2ef71f16f0 Mon Sep 17 00:00:00 2001 From: micha102 Date: Thu, 20 Mar 2025 07:54:19 +0100 Subject: [PATCH 224/491] Update Android SDK detection for latest NDK and select minimum (#4364) Update APP_PLATFORM value for NDK >= r21 Look for the correct folder containing the minimum SDK Switch the default selection to the minimum SDK version if not explicitly given, as per document https://developer.android.com/ndk/guides/application_mk --- configure-android | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/configure-android b/configure-android index ffeb175073..6bb27b0f83 100755 --- a/configure-android +++ b/configure-android @@ -18,8 +18,9 @@ if test "$*" = "--help" -o "$*" = "-h"; then echo "Environment variables:" echo " ANDROID_NDK_ROOT Specify the directory of Android NDK to use." echo " APP_PLATFORM Optionally specify the platform level used, e.g." - echo " android-9. By default, configure will use the" - echo " maximum platform level detected." + echo " android-9. By default, configure will use the :" + echo " - maximum platform level detected if ndk < r21" + echo " - minimum platform level detected if ndk >= r21" echo " TARGET_ABI Optionally specify a single target architecture," echo " e.g. armeabi-v7a, arm64-v8a, mips, x86. By default, " echo " the target architecture is arm64-v8a." @@ -44,6 +45,8 @@ NDK_VER=`sed -n -E 's/^Pkg.Revision *= *([0-9]+).*/\1/p' ${ANDROID_NDK_ROOT}/sou if test "x$APP_PLATFORM" = "x"; then if test -d ${ANDROID_NDK_ROOT}/platforms; then APP_PLATFORM=`ls ${ANDROID_NDK_ROOT}/platforms/ | sed 's/android-//' | sort -gr | head -1` + elif test -f ${ANDROID_NDK_ROOT}/meta/platforms.json; then + APP_PLATFORM=`cat ${ANDROID_NDK_ROOT}/meta/platforms.json | jq -r ".min"` fi if test "x$APP_PLATFORM" != "x"; then APP_PLATFORM="android-${APP_PLATFORM}" From 535b27057ae2a7e946c0fc5284fa7ba106884e00 Mon Sep 17 00:00:00 2001 From: micha102 Date: Mon, 24 Mar 2025 11:10:26 +0100 Subject: [PATCH 225/491] Add support to new Android DNS functions android_res_nquery android_res_nresult (API 29+) (#4333) --- configure-android | 3 +- pjlib/src/pj/addr_resolv_sock.c | 237 ++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+), 1 deletion(-) diff --git a/configure-android b/configure-android index 6bb27b0f83..cca6ce8abd 100755 --- a/configure-android +++ b/configure-android @@ -37,6 +37,7 @@ if test "x${ANDROID_NDK_ROOT}" = "x" || test ! -e ${ANDROID_NDK_ROOT}; then echo "$F error: ANDROID_NDK_ROOT env var is not specified or invalid" exit 0 fi +ENABLE_WEAK_SYMBOLS="-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ -Werror=unguarded-availability" # The BRE version somehow does not work well on MacOSX #NDK_VER=`sed -n -e 's/.*Pkg.Revision *= *\([0-9]\+\).*/\1/p' ${ANDROID_NDK_ROOT}/source.properties` @@ -287,7 +288,7 @@ if test "$1" = "--use-ndk-cflags" || [ "${NDK_VER}" -ge "17" ]; then export CXX="${NDK_CXX}" export CPPFLAGS="${CPPFLAGS}" - export CFLAGS="${NDK_CFLAGS} ${CFLAGS} ${CPPFLAGS}" + export CFLAGS="${NDK_CFLAGS} ${CFLAGS} ${CPPFLAGS} ${ENABLE_WEAK_SYMBOLS}" export CXXFLAGS="${NDK_CXXFLAGS} ${CPPFLAGS}" export LDFLAGS="${NDK_LDFLAGS} ${LDFLAGS}" export LIBS="${LIBS}" diff --git a/pjlib/src/pj/addr_resolv_sock.c b/pjlib/src/pj/addr_resolv_sock.c index e40fe633ea..5b29e30312 100644 --- a/pjlib/src/pj/addr_resolv_sock.c +++ b/pjlib/src/pj/addr_resolv_sock.c @@ -26,8 +26,19 @@ #if defined(PJ_GETADDRINFO_USE_CFHOST) && PJ_GETADDRINFO_USE_CFHOST!=0 # include # include + +#elif defined(PJ_GETADDRINFO_USE_ANDROID) && PJ_GETADDRINFO_USE_ANDROID!=0 +# include +# include +# include +# include +# include +# include #endif +#define THIS_FILE "addr_resolv_sock.c" +#define ANDROID_DNS_TIMEOUT_MS 5000 + PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe) { struct hostent *he; @@ -174,6 +185,232 @@ PJ_DEF(pj_status_t) pj_getaddrinfo(int af, const pj_str_t *nodename, CFRelease(hostname); return status; + +#elif defined(PJ_GETADDRINFO_USE_ANDROID) && PJ_GETADDRINFO_USE_ANDROID!=0 + /* Unlike Android API functions which can be safeguarded with + * __builtin_available, functions from libraries like libc.so + * cannot be loaded directly, even surrounded by a statement + * checking their availability. ns_initparse and ns_parserr are only + * available since Android API 23. This leads to failure of compiling + * for Android API 21. Hence, the two functions are dynamically loaded + * so that the library can be compiled with the minimal API 21. + * Otherwise, PJLIB would only compile with APP_PLATFORM=23 at minimum. + */ + + + if (__builtin_available(android 29, *)) { + /* Define function pointers type for ns_initparse and ns_parserr. + * These functions are in libc.so in Android (Bionic) and not + * in libresolv.so like usually in Linux */ + typedef int (*ns_initparse_fn)(const unsigned char *, int, void *); + typedef int (*ns_parserr_fn)(const void *, int, int, void *); + /* Initialize function pointers to NULL */ + ns_initparse_fn ns_initparse_ptr = NULL; + ns_parserr_fn ns_parserr_ptr = NULL; + /* Load the libc.so library containing network functions */ + void *libc_handle = dlopen("libc.so", RTLD_NOW); + if (libc_handle) { + /* Get the ns_initparse, ns_initparse functions from library */ + ns_initparse_ptr = (ns_initparse_fn) + dlsym(libc_handle, + "ns_initparse"); + ns_parserr_ptr = (ns_parserr_fn) + dlsym(libc_handle, + "ns_parserr"); + + /* Non-null pointers mean DNS functions are properly loaded */ + if (ns_initparse_ptr && + ns_parserr_ptr) { + pj_bzero(&hint, sizeof(hint)); + hint.ai_family = af; + /* Zero value of ai_socktype means the implementation shall + * attempt to resolve the service name for all supported + * socket types */ + hint.ai_socktype = SOCK_STREAM; + + /* Perform asynchronous DNS resolution + * Use NETWORK_UNSPECIFIED lets system choose the network */ + unsigned int netid = NETWORK_UNSPECIFIED; + /* Buffer for the DNS response */ + unsigned char answer[NS_PACKETSZ]; + int rcode = -1; + struct addrinfo *result = NULL, *last = NULL; + + /* Step 1: Send DNS query */ + int resp_handle = android_res_nquery( + netid, nodecopy, ns_c_in, + af == PJ_AF_INET ? ns_t_a : + af == PJ_AF_INET6 ? ns_t_aaaa : + ns_t_a, + 0); + if (resp_handle < 0) { + printf("android_res_nquery() failed\n"); + return PJ_ERESOLVE; + } + + /* Step 2: Wait for response using poll() */ + struct pollfd fds; + fds.fd = resp_handle; + fds.events = POLLIN; + int ret = poll(&fds, 1, ANDROID_DNS_TIMEOUT_MS); + + if (ret <= 0) { + PJ_LOG(4,(THIS_FILE,"Poll timeout or error")); + android_res_cancel(resp_handle); + return PJ_ERESOLVE; + } + /* Step 3: Get DNS response */ + int response_size = android_res_nresult( + resp_handle, &rcode, answer, sizeof(answer)); + if (response_size < 0) { + PJ_LOG(4,(THIS_FILE, + "android_res_nresult() failed")); + return PJ_ERESOLVE; + } + + /* Step 4: Parse the DNS response */ + ns_msg msg; + ns_rr rr; + int num_records, resolved_count = 0; + + if (ns_initparse_ptr(answer, response_size, &msg) < 0) { + PJ_LOG(4,(THIS_FILE, + "Failed to parse DNS response")); + return PJ_ERESOLVE; + } + + num_records = ns_msg_count(msg, ns_s_an); + if (num_records == 0) { + PJ_LOG(4,(THIS_FILE, + "No DNS %s entries found for %s", + af == PJ_AF_INET ? "A" : + af == PJ_AF_INET6 ? "AAAA" : + "A", + nodecopy)); + return PJ_ERESOLVE; + } + + /* Process each answer record */ + for (i = 0; i < num_records && resolved_count < *count; i++) { + if (ns_parserr_ptr(&msg, ns_s_an, i, &rr) < 0) { + continue; + } + + int type = ns_rr_type(rr); + int rdlen = ns_rr_rdlen(rr); + const unsigned char *rdata = ns_rr_rdata(rr); + + /* We handle A records (IPv4) and AAAA records (IPv6) */ + if ((type == ns_t_a && rdlen == 4) || (type == ns_t_aaaa + && rdlen == 16)) { + + /* For IPv4, fill a temporary sockaddr_in */ + /* For IPv6 fill a sockaddr_in6. */ + if (type == ns_t_a) { + struct sockaddr_in addr; + pj_bzero(&addr, sizeof(addr)); + addr.sin_family = PJ_AF_INET; + pj_memcpy(&addr.sin_addr, rdata, 4); + /* Copy the sockaddr into pj_addrinfo.ai_addr */ + pj_memcpy(&ai[resolved_count].ai_addr, + &addr, sizeof(addr)); + } else { + /* type == ns_t_aaaa */ + struct sockaddr_in6 addr6; + pj_bzero(&addr6, sizeof(addr6)); + addr6.sin6_family = PJ_AF_INET6; + pj_memcpy(&addr6.sin6_addr, rdata, 16); + pj_memcpy(&ai[resolved_count].ai_addr, + &addr6, sizeof(addr6)); + } + + /* Store canonical name into ai[].ai_canonname */ + pj_ansi_strxcpy(ai[resolved_count].ai_canonname, + nodename->ptr, + sizeof(ai[resolved_count].ai_canonname)); + resolved_count++; + } + } + + /* Update the count with the number of valid entries found. */ + *count = resolved_count; + + if (resolved_count == 0) { + return PJ_ERESOLVE; + } + return PJ_SUCCESS; + } + } + } + /* Android fallback to getaddrinfo() for API levels < 29 */ + pj_bzero(&hint, sizeof(hint)); + hint.ai_family = af; + /* Zero value of ai_socktype means the implementation shall attempt + * to resolve the service name for all supported socket types */ + hint.ai_socktype = 0; + + rc = getaddrinfo(nodecopy, NULL, &hint, &res); + if (rc != 0) + return PJ_ERESOLVE; + + orig_res = res; + + /* Enumerate each item in the result */ + for (i=0; i<*count && res; res=res->ai_next) { + unsigned j; + pj_bool_t duplicate_found = PJ_FALSE; + + /* Ignore unwanted address families */ + if (af!=PJ_AF_UNSPEC && res->ai_family != af) + continue; + + if (res->ai_socktype != pj_SOCK_DGRAM() && + res->ai_socktype != pj_SOCK_STREAM() && + /* It is possible that the result's sock type + * is unspecified. + */ + res->ai_socktype != 0) + { + continue; + } + + /* Add current address in the resulting list if there + * is no duplicates only. */ + for (j = 0; j < i; j++) { + if (!pj_sockaddr_cmp(&ai[j].ai_addr, res->ai_addr)) { + duplicate_found = PJ_TRUE; + break; + } + } + if (duplicate_found) { + continue; + } + + /* Store canonical name (possibly truncating the name) */ + if (res->ai_canonname) { + pj_ansi_strxcpy(ai[i].ai_canonname, res->ai_canonname, + sizeof(ai[i].ai_canonname)); + } else { + pj_ansi_strxcpy(ai[i].ai_canonname, nodecopy, + sizeof(ai[i].ai_canonname)); + } + + /* Store address */ + PJ_ASSERT_ON_FAIL(res->ai_addrlen <= sizeof(pj_sockaddr), continue); + pj_memcpy(&ai[i].ai_addr, res->ai_addr, res->ai_addrlen); + PJ_SOCKADDR_RESET_LEN(&ai[i].ai_addr); + + /* Next slot */ + ++i; + } + + *count = i; + + freeaddrinfo(orig_res); + + /* Done */ + return (*count > 0? PJ_SUCCESS : PJ_ERESOLVE); + #else /* Call getaddrinfo() */ pj_bzero(&hint, sizeof(hint)); From a6e13db2d440724ce75e53df548e5308804d31d3 Mon Sep 17 00:00:00 2001 From: Alexander Zerpa <57498118+alexander-zerpa@users.noreply.github.com> Date: Mon, 24 Mar 2025 13:10:10 +0100 Subject: [PATCH 226/491] Add options to set `op` and `amf` for aka auth in pjsua cli tool (#4354) --- pjsip-apps/src/pjsua/pjsua_app_common.c | 25 +++++++++ pjsip-apps/src/pjsua/pjsua_app_common.h | 2 + pjsip-apps/src/pjsua/pjsua_app_config.c | 74 ++++++++++++++++++++++++- 3 files changed, 100 insertions(+), 1 deletion(-) diff --git a/pjsip-apps/src/pjsua/pjsua_app_common.c b/pjsip-apps/src/pjsua/pjsua_app_common.c index 702ea96333..221171528d 100644 --- a/pjsip-apps/src/pjsua/pjsua_app_common.c +++ b/pjsip-apps/src/pjsua/pjsua_app_common.c @@ -64,6 +64,31 @@ int my_atoi2(const pj_str_t *str) } } +int my_hex_string_to_octet_array(const char *hex, int len, char octet[]) +{ + int i; + for (i = 0; i < len; i+=2) { + int tmp; + if (i+1 >= len || !pj_isxdigit(hex[i]) || !pj_isxdigit(hex[i+1])) + return i; + tmp = pj_hex_digit_to_val((unsigned char)hex[i]) << 4; + tmp |= pj_hex_digit_to_val((unsigned char)hex[i+1]); + octet[i/2] = (char)(tmp & 0xFF); + } + return len; +} + +void my_octet_array_to_hex_string(const char octet[], int len, char hex[]) +{ + int i; + char *p = hex; + for (i = 0; icred_info[cur_acc->cred_count].data_type |= PJSIP_CRED_DATA_EXT_AKA; cur_acc->cred_info[cur_acc->cred_count].ext.aka.k = pj_str(pj_optarg); cur_acc->cred_info[cur_acc->cred_count].ext.aka.cb = &pjsip_auth_create_aka_response; + break; + + case OPT_AKA_OP: /* aka op */ + { + pj_str_t hex = pj_str(pj_optarg); + pj_str_t *aka_op = &cur_acc->cred_info[cur_acc->cred_count].ext.aka.op; + if (hex.slen/2 <= PJSIP_AKA_OPLEN) { + char* oct = pj_pool_alloc(cfg->pool, hex.slen/2); + int len; + len = my_hex_string_to_octet_array(hex.ptr, hex.slen, oct); + if (len == hex.slen) + pj_strset(aka_op, oct, len/2); + } + if (aka_op->slen != hex.slen/2) { + PJ_LOG(1,(THIS_FILE, "Error: invalid --aka-op value '%s'", + pj_optarg)); + return PJ_EINVAL; + } + } + break; + + case OPT_AKA_AMF: /* aka amf */ + { + pj_str_t hex = pj_str(pj_optarg); + pj_str_t *aka_amf = &cur_acc->cred_info[cur_acc->cred_count].ext.aka.amf; + if (hex.slen/2 <= PJSIP_AKA_AMFLEN) { + char* oct = pj_pool_alloc(cfg->pool, hex.slen/2); + int len; + len = my_hex_string_to_octet_array(hex.ptr, hex.slen, oct); + if (len == hex.slen) + pj_strset(aka_amf, oct, len/2); + } + if (aka_amf->slen != hex.slen/2) { + PJ_LOG(1,(THIS_FILE, "Error: invalid --aka-amf value '%s'", + pj_optarg)); + return PJ_EINVAL; + } + } #endif break; @@ -1813,6 +1861,30 @@ static void write_account_settings(int acc_index, pj_str_t *result) pj_strcat2(result, line); } +#if PJSIP_HAS_DIGEST_AKA_AUTH + if (acc_cfg->cred_info[i].ext.aka.op.slen) { + char hex[PJSIP_AKA_OPLEN * 2]; + pj_str_t *aka_op = &acc_cfg->cred_info[i].ext.aka.op; + pj_assert(aka_op->slen <= PJSIP_AKA_OPLEN); + my_octet_array_to_hex_string(aka_op->ptr, aka_op->slen, hex); + + pj_ansi_snprintf(line, sizeof(line), "--aka-op %.*s\n", + (int)aka_op->slen*2, hex); + pj_strcat2(result, line); + } + + if (acc_cfg->cred_info[i].ext.aka.amf.slen) { + char hex[PJSIP_AKA_AMFLEN * 2]; + pj_str_t *aka_amf = &acc_cfg->cred_info[i].ext.aka.amf; + pj_assert(aka_amf->slen <= PJSIP_AKA_AMFLEN); + my_octet_array_to_hex_string(aka_amf->ptr, aka_amf->slen, hex); + + pj_ansi_snprintf(line, sizeof(line), "--aka-amf %.*s\n", + (int)aka_amf->slen*2, hex); + pj_strcat2(result, line); + } +#endif + if (i != acc_cfg->cred_count - 1) pj_strcat2(result, "--next-cred\n"); } From fa1e6cd88de74bd38d2902611ca4ab49c2e6f529 Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 26 Mar 2025 08:52:46 +0800 Subject: [PATCH 227/491] Support real-time text (RFC4103) (#4344) --- pjmedia/build/Makefile | 2 +- pjmedia/build/pjmedia.vcproj | 108 ++ pjmedia/build/pjmedia.vcxproj | 28 + pjmedia/build/pjmedia.vcxproj.filters | 6 + pjmedia/include/pjmedia.h | 1 + pjmedia/include/pjmedia/config.h | 31 + pjmedia/include/pjmedia/endpoint.h | 39 +- pjmedia/include/pjmedia/rtp.h | 25 + pjmedia/include/pjmedia/stream_common.h | 59 +- pjmedia/include/pjmedia/txt_stream.h | 225 ++++ pjmedia/include/pjmedia/types.h | 3 + pjmedia/src/pjmedia/endpoint.c | 116 ++ pjmedia/src/pjmedia/rtcp_fb.c | 5 +- pjmedia/src/pjmedia/sdp_neg.c | 81 +- pjmedia/src/pjmedia/stream.c | 135 +-- pjmedia/src/pjmedia/stream_common.c | 151 ++- pjmedia/src/pjmedia/stream_imp_common.c | 75 +- pjmedia/src/pjmedia/txt_stream.c | 1056 +++++++++++++++++ pjmedia/src/pjmedia/types.c | 3 +- pjmedia/src/pjmedia/vid_stream.c | 102 +- pjsip-apps/src/3rdparty_media_sample/Makefile | 2 +- .../src/3rdparty_media_sample/alt_pjsua_aud.c | 8 + .../src/3rdparty_media_sample/alt_pjsua_txt.c | 150 +++ pjsip-apps/src/pjsua/pjsua_app.c | 29 +- pjsip-apps/src/pjsua/pjsua_app_cli.c | 35 + pjsip-apps/src/pjsua/pjsua_app_common.h | 4 + pjsip-apps/src/pjsua/pjsua_app_config.c | 25 +- pjsip-apps/src/pjsua/pjsua_app_legacy.c | 42 +- pjsip-apps/src/samples/pjsua2_demo.cpp | 26 +- pjsip/build/Makefile | 2 +- pjsip/build/pjsua_lib.vcproj | 4 + pjsip/build/pjsua_lib.vcxproj | 1 + pjsip/build/pjsua_lib.vcxproj.filters | 3 + pjsip/include/pjsua-lib/pjsua.h | 130 +- pjsip/include/pjsua-lib/pjsua_internal.h | 17 + pjsip/include/pjsua2/account.hpp | 62 +- pjsip/include/pjsua2/call.hpp | 91 +- pjsip/include/pjsua2/endpoint.hpp | 2 + pjsip/src/pjsua-lib/pjsua_aud.c | 110 -- pjsip/src/pjsua-lib/pjsua_call.c | 155 ++- pjsip/src/pjsua-lib/pjsua_core.c | 1 + pjsip/src/pjsua-lib/pjsua_dump.c | 23 +- pjsip/src/pjsua-lib/pjsua_media.c | 118 +- pjsip/src/pjsua-lib/pjsua_txt.c | 262 ++++ pjsip/src/pjsua2/account.cpp | 23 + pjsip/src/pjsua2/call.cpp | 47 +- pjsip/src/pjsua2/endpoint.cpp | 33 + 47 files changed, 3289 insertions(+), 367 deletions(-) create mode 100755 pjmedia/include/pjmedia/txt_stream.h create mode 100755 pjmedia/src/pjmedia/txt_stream.c create mode 100644 pjsip-apps/src/3rdparty_media_sample/alt_pjsua_txt.c create mode 100644 pjsip/src/pjsua-lib/pjsua_txt.c diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile index baa6b258a6..57b1d0a1c1 100644 --- a/pjmedia/build/Makefile +++ b/pjmedia/build/Makefile @@ -72,7 +72,7 @@ export PJMEDIA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ sound_legacy.o sound_port.o stereo_port.o stream_common.o \ stream.o stream_info.o tonegen.o transport_adapter_sample.o \ transport_ice.o transport_loop.o transport_srtp.o transport_udp.o \ - types.o vid_codec.o vid_codec_util.o \ + types.o txt_stream.o vid_codec.o vid_codec_util.o \ vid_port.o vid_stream.o vid_stream_info.o vid_conf.o \ wav_player.o wav_playlist.o wav_writer.o wave.o \ wsola.o audiodev.o videodev.o diff --git a/pjmedia/build/pjmedia.vcproj b/pjmedia/build/pjmedia.vcproj index 28d0d2cb43..ab3f97d0d5 100644 --- a/pjmedia/build/pjmedia.vcproj +++ b/pjmedia/build/pjmedia.vcproj @@ -6550,6 +6550,106 @@ RelativePath="..\src\pjmedia\stream_common.c" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6994,6 +7094,10 @@ /> + + @@ -7687,6 +7791,10 @@ RelativePath="..\include\pjmedia\transport_udp.h" > + + diff --git a/pjmedia/build/pjmedia.vcxproj b/pjmedia/build/pjmedia.vcxproj index 146bfa8ded..1db180fadc 100644 --- a/pjmedia/build/pjmedia.vcxproj +++ b/pjmedia/build/pjmedia.vcxproj @@ -658,6 +658,32 @@ + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + @@ -717,6 +743,7 @@ true + @@ -783,6 +810,7 @@ + diff --git a/pjmedia/build/pjmedia.vcxproj.filters b/pjmedia/build/pjmedia.vcxproj.filters index 222e58cb14..8316c1cd49 100644 --- a/pjmedia/build/pjmedia.vcxproj.filters +++ b/pjmedia/build/pjmedia.vcxproj.filters @@ -170,6 +170,9 @@ Source Files + + Source Files + Source Files @@ -376,6 +379,9 @@ Header Files + + Header Files + Header Files diff --git a/pjmedia/include/pjmedia.h b/pjmedia/include/pjmedia.h index d37c28e736..3f414ee8f1 100644 --- a/pjmedia/include/pjmedia.h +++ b/pjmedia/include/pjmedia.h @@ -67,6 +67,7 @@ #include #include #include +#include #include #include #include diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h index f75029aa0a..24469af4a4 100644 --- a/pjmedia/include/pjmedia/config.h +++ b/pjmedia/include/pjmedia/config.h @@ -939,6 +939,24 @@ #endif +/** + * This macro declares the payload type for T140 text that is advertised + * by PJMEDIA for outgoing SDP. + */ +#ifndef PJMEDIA_RTP_PT_T140 +# define PJMEDIA_RTP_PT_T140 98 +#endif + +/** + * This macro declares the payload type for redundancy that is advertised + * by PJMEDIA for outgoing SDP. Currently, redundancy is only used for + * text stream. + */ +#ifndef PJMEDIA_RTP_PT_REDUNDANCY +# define PJMEDIA_RTP_PT_REDUNDANCY 100 +#endif + + /** * Maximum tones/digits that can be enqueued in the tone generator. */ @@ -1612,6 +1630,19 @@ # endif #endif +/** + * Specify the maximum redundancy levels supported by text stream. + * A value of 1 provides an adequate protection against an average + * packet loss of up to 50%, while 2 can potentially protect + * against 66.7%, so typically setting it to a higher value is + * rarely necessary. + * + * Default: 2, as per the recommendation of RFC 4103. + */ +#ifndef PJMEDIA_TXT_STREAM_MAX_RED_LEVELS +# define PJMEDIA_TXT_STREAM_MAX_RED_LEVELS 2 +#endif + /** * Specify target value for socket receive buffer size. It will be diff --git a/pjmedia/include/pjmedia/endpoint.h b/pjmedia/include/pjmedia/endpoint.h index b00c0b1da9..b2b36d84d0 100644 --- a/pjmedia/include/pjmedia/endpoint.h +++ b/pjmedia/include/pjmedia/endpoint.h @@ -61,7 +61,7 @@ typedef enum pjmedia_endpt_flag /** * This structure specifies various settings that can be passed when creating - * audio/video sdp. + * media sdp. */ typedef struct pjmedia_endpt_create_sdp_param { @@ -72,6 +72,24 @@ typedef struct pjmedia_endpt_create_sdp_param */ pjmedia_dir dir; + /** + * Specifies whether redundancy should be offered in the SDP, + * as specified in RFC 2198. When redundancy is enabled, each + * packet transmission will contain the current data as well as + * a number of the previously transmitted data to provide levels + * of redundancy. This mechanism offers protection against loss + * of data at the cost of additional bandwidth required. + * + * Value is integer indicating redundancy levels, i.e. the number + * of previous data to be included with the current packet. + * (0 means disabled/no redundancy). + * + * Currently it only applies to text media SDP. + * + * Default: 0 + */ + int red_level; + } pjmedia_endpt_create_sdp_param; /** @@ -350,6 +368,25 @@ pjmedia_endpt_create_video_sdp(pjmedia_endpt *endpt, const pjmedia_endpt_create_sdp_param *options, pjmedia_sdp_media **p_m); +/** + * Create SDP media line for text media. + * + * @param endpt The media endpoint. + * @param pool Pool to allocate memory from. + * @param si Socket information. + * @param options Options parameter, can be NULL. If set to NULL, + * default values will be used. + * @param p_m Pointer to receive the created SDP media. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +PJ_DECL(pj_status_t) +pjmedia_endpt_create_text_sdp(pjmedia_endpt *endpt, + pj_pool_t *pool, + const pjmedia_sock_info *si, + const pjmedia_endpt_create_sdp_param *options, + pjmedia_sdp_media **p_m); + /** * Dump media endpoint capabilities. * diff --git a/pjmedia/include/pjmedia/rtp.h b/pjmedia/include/pjmedia/rtp.h index db4b50a332..af8c9b6620 100644 --- a/pjmedia/include/pjmedia/rtp.h +++ b/pjmedia/include/pjmedia/rtp.h @@ -105,11 +105,36 @@ struct pjmedia_rtp_hdr }; #pragma pack() +/** + * Following the RTP header are a number of additional headers, which + * can be used for purposes such as redundancy. + * This structure contains the first 8-bit of an additional header. + */ +#pragma pack(1) +struct pjmedia_rtp_add_hdr_short +{ +#if defined(PJ_IS_BIG_ENDIAN) && (PJ_IS_BIG_ENDIAN!=0) + pj_uint16_t f:1; /**< F bit (0 means last, 1 has more) */ + pj_uint16_t pt:7; /**< payload type */ +#else + pj_uint16_t pt:7; /**< payload type */ + pj_uint16_t f:1; /**< F bit (0 means last, 1 has more) */ +#endif +}; +#pragma pack() + /** * @see pjmedia_rtp_hdr */ typedef struct pjmedia_rtp_hdr pjmedia_rtp_hdr; +/* RTP additional header. */ +typedef pj_uint32_t pjmedia_rtp_add_hdr; + +/** + * @see pjmedia_rtp_add_hdr_short + */ +typedef struct pjmedia_rtp_add_hdr_short pjmedia_rtp_add_hdr_short; /** * RTP extension header. diff --git a/pjmedia/include/pjmedia/stream_common.h b/pjmedia/include/pjmedia/stream_common.h index 5b4c0da312..b786929ace 100644 --- a/pjmedia/include/pjmedia/stream_common.h +++ b/pjmedia/include/pjmedia/stream_common.h @@ -159,7 +159,7 @@ typedef struct pjmedia_stream_common * Media channel is unidirectional flow of media from sender to * receiver. */ -typedef struct pjmedia_channel +struct pjmedia_channel { pjmedia_stream_common *stream; /**< Parent stream. */ pjmedia_dir dir; /**< Channel direction. */ @@ -169,7 +169,7 @@ typedef struct pjmedia_channel void *buf; /**< Output buffer. */ unsigned buf_size; /**< Size of output buffer. */ pjmedia_rtp_session rtp; /**< RTP session. */ -} pjmedia_channel; +}; /** @@ -195,9 +195,22 @@ typedef struct pjmedia_stream_rtp_sess_info } pjmedia_stream_rtp_sess_info; +/** + * Start the media stream. This will start the appropriate channels + * in the media stream, depending on the media direction that was set + * when the stream was created. + * + * @param stream The media stream. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_stream_common_start(pjmedia_stream_common *stream); + + /** * Get the stream statistics. See also - * #pjmedia_stream_get_stat_jbuf() + * #pjmedia_stream_common_get_stat_jbuf() * * @param stream The media stream. * @param stat Media stream statistics. @@ -209,6 +222,20 @@ pjmedia_stream_common_get_stat( const pjmedia_stream_common *stream, pjmedia_rtcp_stat *stat); +/** + * Get current jitter buffer state. See also + * #pjmedia_stream_common_get_stat() + * + * @param stream The media stream. + * @param state Jitter buffer state. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_stream_common_get_stat_jbuf(const pjmedia_stream_common *stream, + pjmedia_jb_state *state); + + /** * Reset the stream statistics. * @@ -360,6 +387,11 @@ pjmedia_stream_ka_config_default(pjmedia_stream_ka_config *cfg); \ unsigned tx_pt; /**< Outgoing codec payload type. */ \ unsigned rx_pt; /**< Incoming codec payload type. */ \ +\ + unsigned tx_red_pt; /**< Outgoing pt for redundancy. */ \ + int tx_red_level;/**< Outgoing redundancy level. */ \ + unsigned rx_red_pt; /**< Incoming pt for redundancy. */ \ + int rx_red_level;/**< Incoming redundancy level. */ \ \ pj_uint32_t ssrc; /**< RTP SSRC. */ \ pj_str_t cname; /**< RTCP CNAME. */ \ @@ -395,10 +427,10 @@ pjmedia_stream_ka_config_default(pjmedia_stream_ka_config *cfg); * corresponds to one "m=" line in SDP session descriptor, and it has * its own RTP/RTCP socket pair. */ -typedef struct pjmedia_stream_info_common +struct pjmedia_stream_info_common { PJ_DECL_STREAM_INFO_COMMON_MEMBER() -} pjmedia_stream_info_common; +}; /** @@ -426,6 +458,23 @@ pjmedia_stream_info_common_from_sdp(pjmedia_stream_info_common *si, pj_bool_t *active); +/** + * This is internal function for parsing redundancy. + * + * @param si Stream info structure to store the result. + * @param pool Pool to allocate memory. + * @param local Local SDP media descriptor. + * @param remote Remote SDP media descriptor. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_stream_info_common_parse_redundancy(pjmedia_stream_info_common *si, + pj_pool_t *pool, + const pjmedia_sdp_media *local_m, + const pjmedia_sdp_media *rem_m); + + /** * This is internal function for parsing SDP format parameter of specific * format or payload type, used by stream in generating stream info from SDP. diff --git a/pjmedia/include/pjmedia/txt_stream.h b/pjmedia/include/pjmedia/txt_stream.h new file mode 100755 index 0000000000..ef8c3eea82 --- /dev/null +++ b/pjmedia/include/pjmedia/txt_stream.h @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2024-2025 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PJMEDIA_TXT_STREAM_H__ +#define __PJMEDIA_TXT_STREAM_H__ + + +/** + * @file txt_stream.h + * @brief Text Stream. + */ + +#include +#include +#include +#include +#include +#include +#include + +PJ_BEGIN_DECL + + +/** + * @defgroup PJMED_TXT_STRM Text streams + * @ingroup PJMEDIA_PORT + * @brief Text communication via the network + * @{ + * + * A text stream is a bidirectional real-time text (RTT) communication + * between two endpoints as specified in RFC 4103. It corresponds to + * a text media description ("m=text" line) in SDP session descriptor. + * + * A text stream consists of two unidirectional channels: + * - encoding channel, which transmits unidirectional text to remote, and + * - decoding channel, which receives unidirectional text from remote. + * + * A text stream internally manages the following objects: + * - a @ref PJMED_JBUF, + * - two instances of RTP sessions (#pjmedia_rtp_session, one for each + * direction), + * - one instance of RTCP session (#pjmedia_rtcp_session), + * - and a reference to media transport to send and receive packets + * to/from the network (see @ref PJMEDIA_TRANSPORT). + * + * Text streams are created by calling #pjmedia_txt_stream_create(), + * specifying #pjmedia_txt_stream_info structure in the parameter. Application + * can construct the #pjmedia_txt_stream_info structure manually, or use + * #pjmedia_txt_stream_info_from_sdp() function to construct the + * #pjmedia_txt_stream_info from local and remote SDP session descriptors. + */ + + +/** + * This structure describes text stream information. Each text stream + * corresponds to one "m=text" line in SDP session descriptor, and it has + * its own RTP/RTCP socket pair. + */ +typedef struct pjmedia_txt_stream_info +{ + PJ_DECL_STREAM_INFO_COMMON_MEMBER() + + pjmedia_codec_info fmt; /**< Incoming codec format info. */ + pjmedia_codec_fmtp enc_fmtp; /**< Encoder's fmtp params. */ + pjmedia_codec_fmtp dec_fmtp; /**< Decoder's fmtp params. */ + unsigned buffer_time;/**< Buffering time. */ +} pjmedia_txt_stream_info; + + +/** + * This structure describes the text data passed in the callback set + * via #pjmedia_txt_stream_set_rx_callback(). + */ +typedef struct pjmedia_txt_stream_data { + int seq; /**< Sequence. */ + pj_uint32_t ts; /**< Timestamp. */ + pj_str_t text; /**< The incoming text data. */ +} pjmedia_txt_stream_data; + + +/** + * This function will initialize the text stream info based on information + * in both SDP session descriptors for the specified text stream index. + * + * @param si Text stream info structure to be initialized. + * @param pool Pool to allocate memory. + * @param endpt PJMEDIA endpoint instance. + * @param local Local SDP session descriptor. + * @param remote Remote SDP session descriptor. + * @param stream_idx Text media stream index in the session descriptor. + * + * @return PJ_SUCCESS if stream info is successfully initialized. + */ +PJ_DECL(pj_status_t) +pjmedia_txt_stream_info_from_sdp( pjmedia_txt_stream_info *si, + pj_pool_t *pool, + pjmedia_endpt *endpt, + const pjmedia_sdp_session *local, + const pjmedia_sdp_session *remote, + unsigned stream_idx); + + +/* + * Opaque declaration for text stream. + */ +typedef struct pjmedia_txt_stream pjmedia_txt_stream; + + +/** + * Create a text stream based on the specified parameter. + * + * @param endpt Media endpoint. + * @param pool Pool to allocate memory for the stream. + * @param info Stream information. + * @param tp Media transport instance used to transmit + * and receive RTP/RTCP packets to/from the underlying + * transport. + * @param user_data Arbitrary user data (for future callback feature). + * @param p_stream Pointer to receive the text stream. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_txt_stream_create(pjmedia_endpt *endpt, + pj_pool_t *pool, + const pjmedia_txt_stream_info *info, + pjmedia_transport *tp, + void *user_data, + pjmedia_txt_stream **p_stream); + + +/** + * Destroy the text stream. + * + * @param stream The text stream. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_txt_stream_destroy(pjmedia_txt_stream *stream); + + +/** + * Send text using the text stream. + * + * @param stream The text stream. + * @param text Text to be sent. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_txt_stream_send_text(pjmedia_txt_stream *stream, const pj_str_t *text); + + +/** + * Start the text stream. This will start the appropriate channels + * in the text stream, depending on the media direction that was set + * when the stream was created. + * + * @param stream The text stream. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_txt_stream_start(pjmedia_txt_stream *stream); + + +/** + * Set callback to be called upon receiving text data. + * + * IMPORTANT: Application shall not destroy the text stream from within + * the callback. + * + * @param stream The text stream. + * @param cb Callback to be called upon receiving text data. + * See #pjmedia_txt_stream_data. + * @param user_data User data to be returned back when the callback + * is called. + * @param option Option, must be 0 for now. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_txt_stream_set_rx_callback(pjmedia_txt_stream *stream, + void (*cb)(pjmedia_txt_stream*, + void *user_data, + const pjmedia_txt_stream_data *data), + void *user_data, + unsigned option); + + +/** + * Get the stream info. + * + * @param stream The text stream. + * @param info Stream info. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_txt_stream_get_info(const pjmedia_txt_stream *stream, + pjmedia_txt_stream_info *info); + + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJMEDIA_STREAM_H__ */ diff --git a/pjmedia/include/pjmedia/types.h b/pjmedia/include/pjmedia/types.h index 8ff724b5f4..8df5f43907 100644 --- a/pjmedia/include/pjmedia/types.h +++ b/pjmedia/include/pjmedia/types.h @@ -61,6 +61,9 @@ typedef enum pjmedia_type /** The media is video. */ PJMEDIA_TYPE_VIDEO, + /** The media is text. */ + PJMEDIA_TYPE_TEXT, + /** The media is application. */ PJMEDIA_TYPE_APPLICATION, diff --git a/pjmedia/src/pjmedia/endpoint.c b/pjmedia/src/pjmedia/endpoint.c index 62f3a7b42d..ddf933aef7 100644 --- a/pjmedia/src/pjmedia/endpoint.c +++ b/pjmedia/src/pjmedia/endpoint.c @@ -99,6 +99,9 @@ struct pjmedia_endpt /** Is telephone-event enable */ pj_bool_t has_telephone_event; + /** Redundancy level */ + unsigned red_level; + /** List of exit callback. */ exit_cb exit_cb_list; }; @@ -107,6 +110,7 @@ struct pjmedia_endpt PJ_DEF(void) pjmedia_endpt_create_sdp_param_default(pjmedia_endpt_create_sdp_param *param) { + pj_bzero(param, sizeof(*param)); param->dir = PJMEDIA_DIR_ENCODING_DECODING; } @@ -907,6 +911,118 @@ pjmedia_endpt_create_video_sdp(pjmedia_endpt *endpt, #endif /* PJMEDIA_HAS_VIDEO */ +/* Create rtpmap and fmtp for redundancy.*/ +static pj_status_t create_redundancy_rtpmap(pj_pool_t *pool, + pjmedia_sdp_media *m, + unsigned red_level, + unsigned red_pt, + unsigned clock_rate, + unsigned media_pt) +{ + const pj_str_t STR_RED = { "red", 3 }; + enum { MAX_FMTP_STR_LEN = 32 }; + char buf[MAX_FMTP_STR_LEN]; + pjmedia_sdp_attr *attr; + pjmedia_sdp_rtpmap rtpmap; + unsigned i, len, buf_len = 0; + pj_str_t *fmt; + + pj_bzero(&rtpmap, sizeof(rtpmap)); + fmt = &m->desc.fmt[m->desc.fmt_count++]; + fmt->ptr = (char*) pj_pool_alloc(pool, 8); + fmt->slen = pj_utoa(red_pt, fmt->ptr); + rtpmap.pt = *fmt; + + /* Add rtpmap. */ + rtpmap.enc_name = STR_RED; + rtpmap.clock_rate = clock_rate; + pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr); + m->attr[m->attr_count++] = attr; + + /* Add fmtp */ + attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr); + attr->name = pj_str("fmtp"); + len = pj_ansi_snprintf(buf, sizeof(buf), "%d %d", + red_pt, media_pt); + buf_len = PJ_MIN(buf_len + len, MAX_FMTP_STR_LEN); + + /* For redundancy, format parameters is a slash "/" + * separated list of RTP payload types. + */ + for (i = 0; i < red_level; i++) { + len = pj_ansi_snprintf(buf + buf_len, MAX_FMTP_STR_LEN - buf_len, + "/%d", media_pt); + buf_len = PJ_MIN(buf_len + len, MAX_FMTP_STR_LEN); + } + attr->value = pj_strdup3(pool, buf); + m->attr[m->attr_count++] = attr; + + return PJ_SUCCESS; +} + +/* Create m=text SDP media line */ +PJ_DEF(pj_status_t) +pjmedia_endpt_create_text_sdp(pjmedia_endpt *endpt, + pj_pool_t *pool, + const pjmedia_sock_info *si, + const pjmedia_endpt_create_sdp_param *options, + pjmedia_sdp_media **p_m) +{ + const pj_str_t STR_TEXT = { "text", 4 }; + pjmedia_sdp_media *m; + pjmedia_sdp_attr *attr; + pjmedia_endpt_create_sdp_param param; + pj_status_t status; + + PJ_UNUSED_ARG(endpt); + + /* Create and init basic SDP media */ + pjmedia_endpt_create_sdp_param_default(¶m); + m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media); + status = init_sdp_media(m, pool, &STR_TEXT, si, options? options->dir: + param.dir); + if (status != PJ_SUCCESS) + return status; + + /* The payload types in m line are listed in order of preference, + * so if we have redundancy, we should add it first since it's + * preferred. + */ + if (options && options->red_level > 0) { + status = create_redundancy_rtpmap(pool, m, options->red_level, + PJMEDIA_RTP_PT_REDUNDANCY, 1000, + PJMEDIA_RTP_PT_T140); + if (status != PJ_SUCCESS) + return status; + } + + /* Add format and rtpmap for T140 text codec */ + { + const pj_str_t STR_T140 = { "t140", 4 }; + pjmedia_sdp_rtpmap rtpmap; + pj_str_t *fmt; + + pj_bzero(&rtpmap, sizeof(rtpmap)); + fmt = &m->desc.fmt[m->desc.fmt_count++]; + fmt->ptr = (char*) pj_pool_alloc(pool, 8); + fmt->slen = pj_utoa(PJMEDIA_RTP_PT_T140, fmt->ptr); + rtpmap.pt = *fmt; + + /* Encoding name */ + rtpmap.enc_name = STR_T140; + + /* Clock rate. Must be 1000 for t140. */ + rtpmap.clock_rate = 1000; + + pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr); + m->attr[m->attr_count++] = attr; + } + + *p_m = m; + return PJ_SUCCESS; +} + + /** * Create a "blank" SDP session description. The SDP will contain basic SDP * fields such as origin, time, and name, but without any media lines. diff --git a/pjmedia/src/pjmedia/rtcp_fb.c b/pjmedia/src/pjmedia/rtcp_fb.c index ecb243ed61..194b604893 100644 --- a/pjmedia/src/pjmedia/rtcp_fb.c +++ b/pjmedia/src/pjmedia/rtcp_fb.c @@ -340,8 +340,11 @@ static pj_status_t get_codec_info_from_sdp(pjmedia_endpt *endpt, pj_status_t status; type = pjmedia_get_type(&m->desc.media); - if (type != PJMEDIA_TYPE_AUDIO && type != PJMEDIA_TYPE_VIDEO) + if (type != PJMEDIA_TYPE_AUDIO && type != PJMEDIA_TYPE_VIDEO && + type != PJMEDIA_TYPE_TEXT) + { return PJMEDIA_EUNSUPMEDIATYPE; + } codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); for (j = 0; j < m->desc.fmt_count && cnt < *sci_cnt; ++j) { diff --git a/pjmedia/src/pjmedia/sdp_neg.c b/pjmedia/src/pjmedia/sdp_neg.c index dead49a1b9..5007d4f498 100644 --- a/pjmedia/src/pjmedia/sdp_neg.c +++ b/pjmedia/src/pjmedia/sdp_neg.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1191,6 +1192,7 @@ static pj_status_t match_offer(pj_pool_t *pool, pj_bool_t master_has_codec = 0, master_has_other = 0, found_matching_codec = 0, + found_matching_red = 0, found_matching_telephone_event = 0, found_matching_other = 0; unsigned pt_answer_count = 0; @@ -1198,6 +1200,7 @@ static pj_status_t match_offer(pj_pool_t *pool, pj_str_t pt_offer[PJMEDIA_MAX_SDP_FMT]; pjmedia_sdp_media *answer; const pjmedia_sdp_media *master, *slave; + int o_red_level = 0; unsigned nclockrate = 0, clockrate[PJMEDIA_MAX_SDP_FMT]; unsigned ntel_clockrate = 0, tel_clockrate[PJMEDIA_MAX_SDP_FMT]; @@ -1285,7 +1288,7 @@ static pj_status_t match_offer(pj_pool_t *pool, */ const pjmedia_sdp_attr *a; pjmedia_sdp_rtpmap or_; - pj_bool_t is_codec = 0; + pj_bool_t is_codec = 0, is_tel = 0, is_red = 0; /* Get the rtpmap for the payload type in the master. */ a = pjmedia_sdp_media_find_attr2(master, "rtpmap", @@ -1296,14 +1299,18 @@ static pj_status_t match_offer(pj_pool_t *pool, } pjmedia_sdp_attr_get_rtpmap(a, &or_); - if (pj_stricmp2(&or_.enc_name, "telephone-event")) { + if (!pj_stricmp2(&or_.enc_name, "telephone-event")) { + is_tel = 1; + } else if (!pj_stricmp2(&or_.enc_name, "red")) { + is_red = 1; + } else { master_has_codec = 1; if (!answer_with_multiple_codecs && found_matching_codec) continue; is_codec = 1; } - /* Find paylaod in our initial SDP with matching + /* Find payload in our initial SDP with matching * encoding name and clock rate. */ for (j=0; jdesc.fmt_count; ++j) { @@ -1352,7 +1359,7 @@ static pj_status_t match_offer(pj_pool_t *pool, break; if (k == nclockrate) clockrate[nclockrate++] = or_.clock_rate; - } else { + } else if (is_tel) { unsigned k; /* Keep track of tel-event clock rate, @@ -1366,6 +1373,22 @@ static pj_status_t match_offer(pj_pool_t *pool, tel_clockrate[ntel_clockrate++] = or_.clock_rate; found_matching_telephone_event = 1; + } else if (is_red) { + pjmedia_sdp_media *o_med; + unsigned o_fmt_idx; + pjmedia_codec_fmtp fmtp; + pj_status_t status; + + o_med = (pjmedia_sdp_media *)offer; + o_fmt_idx = prefer_remote_codec_order? i:j; + pt = pj_strtoul(&o_med->desc.fmt[o_fmt_idx]); + status = pjmedia_stream_info_parse_fmtp( + pool, o_med, pt, &fmtp); + if (status != PJ_SUCCESS) + continue; + + o_red_level = fmtp.cnt - 1; + found_matching_red = 1; } pt_offer[pt_answer_count] = @@ -1428,6 +1451,56 @@ static pj_status_t match_offer(pj_pool_t *pool, /* Seems like everything is in order. */ + /* Match offer's redundancy level. */ + if (found_matching_red) { + for (i = 0; i < pt_answer_count; i++) { + pjmedia_sdp_attr *a; + pjmedia_sdp_rtpmap r; + pjmedia_codec_fmtp fmtp; + int a_red_level = 0; + unsigned pt; + pj_status_t status; + + /* Get the rtpmap. */ + a = pjmedia_sdp_media_find_attr2(preanswer, "rtpmap", + &preanswer->desc.fmt[i]); + pj_assert(a); + pjmedia_sdp_attr_get_rtpmap(a, &r); + + /* Only care for redundancy format */ + if (pj_stricmp2(&r.enc_name, "red")) + continue; + + pt = pj_strtoul(&preanswer->desc.fmt[i]); + status = pjmedia_stream_info_parse_fmtp(pool, preanswer, + pt, &fmtp); + if (status != PJ_SUCCESS) + break; + + /* If we support higher redundancy level, reduce it to match + * offer. + */ + a_red_level = fmtp.cnt - 1; + if (a_red_level > o_red_level) { + a = pjmedia_sdp_media_find_attr2(preanswer, "fmtp", + &preanswer->desc.fmt[i]); + if (a) { + int lvl = 0; + for (i = 0; i < (unsigned)a->value.slen; i++) { + if (*(a->value.ptr + i) == '/') { + if (lvl++ >= o_red_level) { + a->value.slen = i; + break; + } + } + } + } + } + + break; + } + } + /* Remove unwanted telephone-event formats. */ if (found_matching_telephone_event) { pj_str_t first_televent_offer = {0}; diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index 5bfc261b33..1193c5dde3 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -1621,90 +1621,6 @@ static pj_status_t on_stream_rx_rtp(pjmedia_stream_common *c_strm, return status; } - -/* - * Create media channel. - */ -static pj_status_t create_channel( pj_pool_t *pool, - pjmedia_stream *stream, - pjmedia_dir dir, - unsigned pt, - const pjmedia_stream_info *param, - pjmedia_channel **p_channel) -{ - pjmedia_stream_common *c_strm = &stream->base; - pjmedia_channel *channel; - pj_status_t status; - - /* Allocate memory for channel descriptor */ - - channel = PJ_POOL_ZALLOC_T(pool, pjmedia_channel); - PJ_ASSERT_RETURN(channel != NULL, PJ_ENOMEM); - - /* Init channel info. */ - - channel->stream = c_strm; - channel->dir = dir; - channel->paused = 1; - channel->pt = pt; - - - /* Allocate buffer for outgoing packet. */ - - if (param->type == PJMEDIA_TYPE_AUDIO) { - unsigned max_rx_based_size; - unsigned max_bps_based_size; - - /* buf buffer is used for sending and receiving, so lets calculate - * its size based on both. For receiving, we have c_strm->frame_size, - * which is used in configuring jitter buffer frame length. - * For sending, it is based on codec max_bps info. - */ - max_rx_based_size = c_strm->frame_size; - max_bps_based_size = stream->codec_param.info.max_bps * - PJMEDIA_MAX_FRAME_DURATION_MS / 8 / 1000; - channel->buf_size = PJ_MAX(max_rx_based_size, max_bps_based_size); - - /* Also include RTP header size (for sending) */ - channel->buf_size += sizeof(pjmedia_rtp_hdr); - - if (channel->buf_size > PJMEDIA_MAX_MTU - - PJMEDIA_STREAM_RESV_PAYLOAD_LEN) - { - channel->buf_size = PJMEDIA_MAX_MTU - - PJMEDIA_STREAM_RESV_PAYLOAD_LEN; - } - } else { - return PJ_ENOTSUP; - } - - channel->buf = pj_pool_alloc(pool, channel->buf_size); - PJ_ASSERT_RETURN(channel->buf != NULL, PJ_ENOMEM); - - - - /* Create RTP and RTCP sessions: */ - { - pjmedia_rtp_session_setting settings; - - settings.flags = (pj_uint8_t)((param->rtp_seq_ts_set << 2) | - (param->has_rem_ssrc << 4) | 3); - settings.default_pt = pt; - settings.sender_ssrc = param->ssrc; - settings.peer_ssrc = param->rem_ssrc; - settings.seq = param->rtp_seq; - settings.ts = param->rtp_ts; - status = pjmedia_rtp_session_init2(&channel->rtp, settings); - } - if (status != PJ_SUCCESS) - return status; - - /* Done. */ - *p_channel = channel; - return PJ_SUCCESS; -} - - /* * Handle events. */ @@ -1753,6 +1669,9 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, pjmedia_audio_format_detail *afd; pj_pool_t *own_pool = NULL; char *p; + unsigned max_rx_based_size; + unsigned max_bps_based_size; + unsigned buf_size; pj_status_t status; pjmedia_transport_attach_param att_param; @@ -2125,18 +2044,26 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, pjmedia_jbuf_set_adaptive( c_strm->jb, jb_init, jb_min_pre, jb_max_pre); pjmedia_jbuf_set_discard(c_strm->jb, info->jb_discard_algo); - /* Create decoder channel: */ + /* buf buffer is used for sending and receiving, so lets calculate + * its size based on both. For receiving, we have c_strm->frame_size, + * which is used in configuring jitter buffer frame length. + * For sending, it is based on codec max_bps info. + */ + max_rx_based_size = c_strm->frame_size; + max_bps_based_size = stream->codec_param.info.max_bps * + PJMEDIA_MAX_FRAME_DURATION_MS / 8 / 1000; + buf_size = PJ_MAX(max_rx_based_size, max_bps_based_size); - status = create_channel( pool, stream, PJMEDIA_DIR_DECODING, - info->rx_pt, info, &c_strm->dec); + /* Create decoder channel: */ + status = create_channel( pool, c_strm, PJMEDIA_DIR_DECODING, + info->rx_pt, buf_size, c_strm->si, &c_strm->dec); if (status != PJ_SUCCESS) goto err_cleanup; /* Create encoder channel: */ - - status = create_channel( pool, stream, PJMEDIA_DIR_ENCODING, - info->tx_pt, info, &c_strm->enc); + status = create_channel( pool, c_strm, PJMEDIA_DIR_ENCODING, + info->tx_pt, buf_size, c_strm->si, &c_strm->enc); if (status != PJ_SUCCESS) goto err_cleanup; @@ -2530,27 +2457,7 @@ PJ_DEF(pjmedia_transport*) pjmedia_stream_get_transport(pjmedia_stream *st) */ PJ_DEF(pj_status_t) pjmedia_stream_start(pjmedia_stream *stream) { - pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; - - PJ_ASSERT_RETURN(stream && c_strm->enc && c_strm->dec, PJ_EINVALIDOP); - - if (c_strm->enc && (c_strm->dir & PJMEDIA_DIR_ENCODING)) { - c_strm->enc->paused = 0; - //pjmedia_snd_stream_start(c_strm->enc->snd_stream); - PJ_LOG(4,(c_strm->port.info.name.ptr, "Encoder stream started")); - } else { - PJ_LOG(4,(c_strm->port.info.name.ptr, "Encoder stream paused")); - } - - if (c_strm->dec && (c_strm->dir & PJMEDIA_DIR_DECODING)) { - c_strm->dec->paused = 0; - //pjmedia_snd_stream_start(c_strm->dec->snd_stream); - PJ_LOG(4,(c_strm->port.info.name.ptr, "Decoder stream started")); - } else { - PJ_LOG(4,(c_strm->port.info.name.ptr, "Decoder stream paused")); - } - - return PJ_SUCCESS; + return pjmedia_stream_common_start((pjmedia_stream_common *) stream); } /* @@ -2620,10 +2527,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_stat_xr( const pjmedia_stream *stream, PJ_DEF(pj_status_t) pjmedia_stream_get_stat_jbuf(const pjmedia_stream *stream, pjmedia_jb_state *state) { - const pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; - - PJ_ASSERT_RETURN(stream && state, PJ_EINVAL); - return pjmedia_jbuf_get_state(c_strm->jb, state); + return pjmedia_stream_common_get_stat_jbuf((pjmedia_stream_common *)stream, + state); } /* diff --git a/pjmedia/src/pjmedia/stream_common.c b/pjmedia/src/pjmedia/stream_common.c index 6127a02f6a..192edf701b 100644 --- a/pjmedia/src/pjmedia/stream_common.c +++ b/pjmedia/src/pjmedia/stream_common.c @@ -30,6 +30,7 @@ static const pj_str_t ID_IN = { "IN", 2 }; static const pj_str_t ID_IP4 = { "IP4", 3}; static const pj_str_t ID_IP6 = { "IP6", 3}; +static const pj_str_t ID_RTPMAP = { "rtpmap", 6 }; /* * Create stream info from SDP media line. @@ -268,25 +269,19 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_common_from_sdp( si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; si->jb_discard_algo = PJMEDIA_JB_DISCARD_PROGRESSIVE; - if (pjmedia_get_type(&local_m->desc.media) == PJMEDIA_TYPE_AUDIO || - pjmedia_get_type(&local_m->desc.media) == PJMEDIA_TYPE_VIDEO) - { - /* Get local RTCP-FB info */ - if (pjmedia_get_type(&local_m->desc.media) == PJMEDIA_TYPE_AUDIO || - pjmedia_get_type(&local_m->desc.media) == PJMEDIA_TYPE_VIDEO) - status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, local, - stream_idx, si->rx_pt, - &si->loc_rtcp_fb); - if (status != PJ_SUCCESS) - return status; + /* Get local RTCP-FB info */ + status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, local, + stream_idx, si->rx_pt, + &si->loc_rtcp_fb); + if (status != PJ_SUCCESS) + return status; - /* Get remote RTCP-FB info */ - status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, remote, - stream_idx, si->tx_pt, - &si->rem_rtcp_fb); - if (status != PJ_SUCCESS) - return status; - } + /* Get remote RTCP-FB info */ + status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, remote, + stream_idx, si->tx_pt, + &si->rem_rtcp_fb); + if (status != PJ_SUCCESS) + return status; *active = PJ_TRUE; return status; @@ -364,7 +359,7 @@ PJ_DECL(pj_status_t) pjmedia_stream_info_parse_fmtp_data(pj_pool_t *pool, /* Get token */ start = p; - while (p < p_end && *p != ';') ++p; + while (p < p_end && *p != ';' && *p != '/') ++p; end = p - 1; /* Right trim */ @@ -421,6 +416,89 @@ PJ_DECL(pj_status_t) pjmedia_stream_info_parse_fmtp_data(pj_pool_t *pool, return PJ_SUCCESS; } +static pj_status_t parse_redundancy(pj_pool_t *pool, + const pjmedia_sdp_media *sdp, + unsigned media_pt, + unsigned *red_pt, + int *red_level) +{ + static const pj_str_t ID_REDUNDANCY = { "red", 3 }; + unsigned i, pt = 0; + int level = 0; + pjmedia_codec_fmtp fmtp; + pj_status_t status; + + /* Get payload type for redundancy */ + for (i = 0; i < sdp->attr_count; ++i) { + const pjmedia_sdp_attr *attr; + pjmedia_sdp_rtpmap r; + + attr = sdp->attr[i]; + if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) + continue; + if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) + continue; + if (pj_strcmp(&r.enc_name, &ID_REDUNDANCY) == 0) { + pt = pj_strtoul(&r.pt); + break; + } + } + if (pt == 0) + return PJ_ENOTFOUND; + + status = pjmedia_stream_info_parse_fmtp(pool, sdp, pt, &fmtp); + if (status != PJ_SUCCESS) + return status; + + for (i = 0; i < fmtp.cnt; i++, level++) { + unsigned med_pt = pj_strtoul(&fmtp.param[i].val); + + if (med_pt != media_pt) + return PJMEDIA_SDP_EINFMTP; + } + + *red_pt = pt; + *red_level = level - 1; + + return status; +} + +/* + * Internal function for parsing redundancy info from the SDP media. + */ +PJ_DEF(pj_status_t) +pjmedia_stream_info_common_parse_redundancy(pjmedia_stream_info_common *si, + pj_pool_t *pool, + const pjmedia_sdp_media *local_m, + const pjmedia_sdp_media *rem_m) +{ + pj_status_t status; + + /* Get incoming payload type for redundancy */ + status = parse_redundancy(pool, local_m, si->rx_pt, &si->rx_red_pt, + &si->rx_red_level); + if (status != PJ_SUCCESS) + return status; + + /* Get outgoing payload type for redundancy */ + status = parse_redundancy(pool, rem_m, si->tx_pt, &si->tx_red_pt, + &si->tx_red_level); + if (status == PJ_SUCCESS) { + if (si->tx_red_level == -1) { + /* Remote SDP contains "red" rtpmap but no fmtp, meaning that + * they accept our offer but most likely will not send redundancy + * from their side. + */ + si->tx_red_level = si->rx_red_level; + } else if (si->tx_red_level != si->rx_red_level) { + si->tx_red_level = PJ_MIN(si->tx_red_level, si->rx_red_level); + si->rx_red_level = si->tx_red_level; + } + } + + return PJ_SUCCESS; +} + /* * Get stream statistics. */ @@ -447,6 +525,17 @@ pjmedia_stream_common_reset_stat(pjmedia_stream_common *c_strm) return PJ_SUCCESS; } +/* + * Get jitter buffer state. + */ +PJ_DEF(pj_status_t) +pjmedia_stream_common_get_stat_jbuf(const pjmedia_stream_common *c_strm, + pjmedia_jb_state *state) +{ + PJ_ASSERT_RETURN(c_strm && state, PJ_EINVAL); + return pjmedia_jbuf_get_state(c_strm->jb, state); +} + /* * Send RTCP SDES. */ @@ -679,3 +768,27 @@ pj_status_t pjmedia_stream_send_rtcp(pjmedia_stream_common *c_strm, return status; } + +/* + * Start stream. + */ +PJ_DEF(pj_status_t) pjmedia_stream_common_start(pjmedia_stream_common *c_strm) +{ + PJ_ASSERT_RETURN(c_strm && c_strm->enc && c_strm->dec, PJ_EINVALIDOP); + + if (c_strm->enc && (c_strm->dir & PJMEDIA_DIR_ENCODING)) { + c_strm->enc->paused = 0; + PJ_LOG(4,(c_strm->port.info.name.ptr, "Encoder stream started")); + } else { + PJ_LOG(4,(c_strm->port.info.name.ptr, "Encoder stream paused")); + } + + if (c_strm->dec && (c_strm->dir & PJMEDIA_DIR_DECODING)) { + c_strm->dec->paused = 0; + PJ_LOG(4,(c_strm->port.info.name.ptr, "Decoder stream started")); + } else { + PJ_LOG(4,(c_strm->port.info.name.ptr, "Decoder stream paused")); + } + + return PJ_SUCCESS; +} diff --git a/pjmedia/src/pjmedia/stream_imp_common.c b/pjmedia/src/pjmedia/stream_imp_common.c index aa35c6983b..6ea63bfeb4 100755 --- a/pjmedia/src/pjmedia/stream_imp_common.c +++ b/pjmedia/src/pjmedia/stream_imp_common.c @@ -184,6 +184,68 @@ static void trace_jb_put(pjmedia_stream_common *c_strm, #endif /* TRACE_JB */ +/* + * Create media channel. + */ +static pj_status_t create_channel( pj_pool_t *pool, + pjmedia_stream_common *c_strm, + pjmedia_dir dir, + unsigned pt, + unsigned buf_size, + const pjmedia_stream_info_common *param, + pjmedia_channel **p_channel) +{ + pjmedia_channel *channel; + pj_status_t status; + + /* Allocate memory for channel descriptor */ + channel = PJ_POOL_ZALLOC_T(pool, pjmedia_channel); + PJ_ASSERT_RETURN(channel != NULL, PJ_ENOMEM); + + /* Init channel info. */ + channel->stream = c_strm; + channel->dir = dir; + channel->paused = 1; + channel->pt = pt; + + /* Allocate buffer for outgoing packet. */ + channel->buf_size = buf_size; + + /* Also include RTP header size (for sending) */ + channel->buf_size += sizeof(pjmedia_rtp_hdr); + + if (channel->buf_size > PJMEDIA_MAX_MTU - + PJMEDIA_STREAM_RESV_PAYLOAD_LEN) + { + channel->buf_size = PJMEDIA_MAX_MTU - + PJMEDIA_STREAM_RESV_PAYLOAD_LEN; + } + + channel->buf = pj_pool_alloc(pool, channel->buf_size); + PJ_ASSERT_RETURN(channel->buf != NULL, PJ_ENOMEM); + + /* Create RTP and RTCP sessions: */ + { + pjmedia_rtp_session_setting settings; + + settings.flags = (pj_uint8_t)((param->rtp_seq_ts_set << 2) | + (param->has_rem_ssrc << 4) | 3); + settings.default_pt = pt; + settings.sender_ssrc = param->ssrc; + settings.peer_ssrc = param->rem_ssrc; + settings.seq = param->rtp_seq; + settings.ts = param->rtp_ts; + status = pjmedia_rtp_session_init2(&channel->rtp, settings); + } + if (status != PJ_SUCCESS) + return status; + + /* Done. */ + *p_channel = channel; + return PJ_SUCCESS; +} + + static pj_status_t send_rtcp(pjmedia_stream_common *c_strm, pj_bool_t with_sdes, pj_bool_t with_bye, @@ -455,13 +517,15 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) /* Update RTP session (also checks if RTP session can accept * the incoming packet. */ - check_pt = PJMEDIA_STREAM_CHECK_RTP_PT; + check_pt = PJMEDIA_STREAM_CHECK_RTP_PT && hdr->pt != c_strm->si->rx_red_pt; #ifdef AUDIO_STREAM check_pt = check_pt && hdr->pt != stream->rx_event_pt; #endif pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, check_pt); #if !PJMEDIA_STREAM_CHECK_RTP_PT - if (!check_pt && hdr->pt != channel->rtp.out_pt) { + if (!check_pt && hdr->pt != channel->rtp.out_pt && + hdr->pt != c_strm->si->rx_red_pt) + { #ifdef AUDIO_STREAM if (hdr->pt != stream->rx_event_pt) #endif @@ -501,8 +565,11 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) goto on_return; } - /* Ignore if payloadlen is zero */ - if (payloadlen == 0) { + /* Ignore if payloadlen is zero. + * Exception: text stream needs to accept empty text block to + * track sequence number and idle period. + */ + if (c_strm->si->type != PJMEDIA_TYPE_TEXT && payloadlen == 0) { pkt_discarded = PJ_TRUE; goto on_return; } diff --git a/pjmedia/src/pjmedia/txt_stream.c b/pjmedia/src/pjmedia/txt_stream.c new file mode 100755 index 0000000000..60b3f0c78b --- /dev/null +++ b/pjmedia/src/pjmedia/txt_stream.c @@ -0,0 +1,1056 @@ +/* + * Copyright (C) 2024-2025 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define THIS_FILE "txt_stream.c" +#define LOGERR_(expr) PJ_PERROR(4,expr) +#define TRC_(expr) PJ_LOG(5,expr) + +#if 0 +# define TRACE_(log) PJ_LOG(3, log) +#else +# define TRACE_(log) +#endif + +/* Default and max buffering time if not specified. + * The RFC recommends a buffering time of 300 ms. Note that the maximum + * according to the RFC is 500 ms. + */ +#define BUFFERING_TIME 300 +#define MAX_BUFFERING_TIME 500 + +/* Buffer size to store the characters buffered during BUFFERING_TIME. + * The default cps (character per second) is 30, but it's over a 10-second + * interval, so we need a larger buffer to handle the burst. + */ +#define BUFFER_SIZE 64 + +/* The number of buffers must be the max redundancy we want to support + + * plus one more to store the primary data. + */ +#define NUM_BUFFERS PJMEDIA_TXT_STREAM_MAX_RED_LEVELS + 1 + +/* 5.4. Compensation for Packets Out of Order: + * If analysis of a received packet reveals a gap in the sequence, + * we need to wait for the missing packet(s) to arrive. It is + * RECOMMENDED the waiting time be limited to 1 second (1000 ms). + */ +#define MAX_RX_WAITING_TIME 1000 + +/* Maximum number of text packets that can be kept in the jitter buffer. + * The value should be roughly our MAX_RX_WAITING_TIME over remote's + * buffering time (which unfortunately we don't know). + */ +#define JBUF_MAX_COUNT 8 + + +/* Buffer to store redundancy and primary data. */ +typedef struct red_buf +{ + unsigned timestamp; + unsigned length; + char buf[BUFFER_SIZE]; +} red_buf; + +typedef struct pjmedia_txt_stream +{ + pjmedia_stream_common base; + pjmedia_txt_stream_info si; /**< Creation parameter. */ + + pjmedia_clock *clock; /**< Clock. */ + unsigned buf_time; /**< Buffering time. */ + pj_bool_t is_idle; /**< Is idle? */ + + pj_timestamp rtcp_last_tx; /**< Last RTCP tx time. */ + pj_timestamp tx_last_ts; /**< Timestamp of last tx. */ + red_buf tx_buf[NUM_BUFFERS];/**< Tx buffer. */ + int tx_buf_idx; /**< Index to current buffer*/ + int tx_nred; /**< Num of redundant data. */ + + int rx_last_seq; /**< Sequence of last rx. */ + pj_bool_t is_waiting; /**< Is waiting? */ + pj_timestamp rx_wait_ts; /**< Ts of waiting start. */ + + /* Incoming text callback. */ + void (*cb)(pjmedia_txt_stream *, void *, + const pjmedia_txt_stream_data *); + void *cb_user_data; + +} pjmedia_txt_stream; + + +static void clock_cb(const pj_timestamp *ts, void *user_data); + + +#include "stream_imp_common.c" + +static void on_stream_destroy(void *arg) +{ + pjmedia_txt_stream *stream = (pjmedia_txt_stream *)arg; + + if (stream->clock) { + pjmedia_clock_destroy(stream->clock); + stream->clock = NULL; + } +} + +/* + * Destroy stream. + */ +PJ_DEF(pj_status_t) pjmedia_txt_stream_destroy( pjmedia_txt_stream *stream ) +{ + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; + + PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); + + PJ_LOG(4,(c_strm->port.info.name.ptr, "Stream destroying")); + + stream->cb = NULL; + + if (stream->clock) + pjmedia_clock_stop(stream->clock); + + /* Send RTCP BYE (also SDES & XR) */ + if (c_strm->transport && !c_strm->rtcp_sdes_bye_disabled) { +#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) + send_rtcp(c_strm, PJ_TRUE, PJ_TRUE, c_strm->rtcp.xr_enabled, + PJ_FALSE, PJ_FALSE, PJ_FALSE); +#else + send_rtcp(c_strm, PJ_TRUE, PJ_TRUE, PJ_FALSE, PJ_FALSE, + PJ_FALSE, PJ_FALSE); +#endif + } + + /* Unsubscribe from RTCP session events */ + // Currently unused + //pjmedia_event_unsubscribe(NULL, &stream_event_cb, stream, + // &c_strm->rtcp); + + /* Detach from transport + * MUST NOT hold stream mutex while detaching from transport, as + * it may cause deadlock. See ticket #460 for the details. + */ + if (c_strm->transport) { + pjmedia_transport_detach(c_strm->transport, c_strm); + //c_strm->transport = NULL; + } + + if (c_strm->grp_lock) { + pj_grp_lock_dec_ref(c_strm->grp_lock); + } else { + on_destroy(c_strm); + } + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) +pjmedia_txt_stream_create( pjmedia_endpt *endpt, + pj_pool_t *pool, + const pjmedia_txt_stream_info *info, + pjmedia_transport *tp, + void *user_data, + pjmedia_txt_stream **p_stream) + +{ + enum { M = 32 }; + pjmedia_txt_stream *stream; + pjmedia_stream_common *c_strm; + pj_str_t name; + pj_pool_t *own_pool = NULL; + char *p; + unsigned buf_size; + pj_status_t status; + pjmedia_transport_attach_param att_param; + pjmedia_clock_param cparam; + + PJ_ASSERT_RETURN(endpt && info && tp && p_stream, PJ_EINVAL); + + if (pool == NULL) { + own_pool = pjmedia_endpt_create_pool( endpt, "tstrm%p", + 2000, 2000); + PJ_ASSERT_RETURN(own_pool != NULL, PJ_ENOMEM); + pool = own_pool; + } + + /* Allocate the text stream: */ + stream = PJ_POOL_ZALLOC_T(pool, pjmedia_txt_stream); + PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM); + c_strm = &stream->base; + c_strm->own_pool = own_pool; + + /* Duplicate stream info */ + pj_memcpy(&stream->si, info, sizeof(*info)); + c_strm->si = (pjmedia_stream_info_common *)&stream->si; + pj_strdup(pool, &stream->si.fmt.encoding_name, &info->fmt.encoding_name); + pjmedia_rtcp_fb_info_dup(pool, &c_strm->si->loc_rtcp_fb, + &info->loc_rtcp_fb); + pjmedia_rtcp_fb_info_dup(pool, &c_strm->si->rem_rtcp_fb, + &info->rem_rtcp_fb); + + /* Init stream/port name */ + name.ptr = (char*) pj_pool_alloc(pool, M); + name.slen = pj_ansi_snprintf(name.ptr, M, "tstrm%p", stream); + c_strm->port.info.name = name; + + /* Init stream: */ + c_strm->endpt = endpt; + c_strm->dir = info->dir; + c_strm->user_data = user_data; + c_strm->rtcp_interval = (PJMEDIA_RTCP_INTERVAL-500 + (pj_rand()%1000)) * + info->fmt.clock_rate / 1000; + c_strm->rtcp_sdes_bye_disabled = info->rtcp_sdes_bye_disabled; + + c_strm->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME; + c_strm->rtcp_fb_nack.pid = -1; + stream->rx_last_seq = -1; + stream->is_idle = PJ_TRUE; + +#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 + c_strm->use_ka = info->use_ka; + c_strm->ka_interval = info->ka_cfg.ka_interval; + c_strm->start_ka_count = info->ka_cfg.start_count; + c_strm->start_ka_interval = info->ka_cfg.start_interval; +#endif + + c_strm->cname = info->cname; + if (c_strm->cname.slen == 0) { + /* Build random RTCP CNAME. CNAME has user@host format */ + c_strm->cname.ptr = p = (char*) pj_pool_alloc(pool, 20); + pj_create_random_string(p, 5); + p += 5; + *p++ = '@'; *p++ = 'p'; *p++ = 'j'; + pj_create_random_string(p, 6); + p += 6; + *p++ = '.'; *p++ = 'o'; *p++ = 'r'; *p++ = 'g'; + c_strm->cname.slen = p - c_strm->cname.ptr; + } + + /* Init buffering time and create clock. */ + stream->buf_time = info->buffer_time; + if (stream->buf_time == 0) + stream->buf_time = BUFFERING_TIME; + cparam.usec_interval = stream->buf_time * 1000; + cparam.clock_rate = 1000; + status = pjmedia_clock_create2(pool, &cparam, + PJMEDIA_CLOCK_NO_HIGHEST_PRIO, + clock_cb, stream, &stream->clock); + if (status != PJ_SUCCESS) + goto err_cleanup; + + /* Create mutex to protect jitter buffer: */ + status = pj_mutex_create_recursive(pool, NULL, &c_strm->jb_mutex); + if (status != PJ_SUCCESS) + goto err_cleanup; + + /* Create jitter buffer */ + status = pjmedia_jbuf_create(pool, &c_strm->port.info.name, + BUFFER_SIZE, + stream->buf_time, + JBUF_MAX_COUNT, &c_strm->jb); + if (status != PJ_SUCCESS) + goto err_cleanup; + + /* Set up jitter buffer */ + pjmedia_jbuf_set_fixed(c_strm->jb, 0); + pjmedia_jbuf_set_discard(c_strm->jb, PJMEDIA_JB_DISCARD_NONE); + + /* Create decoder channel: */ + status = create_channel( pool, c_strm, PJMEDIA_DIR_DECODING, + info->rx_pt, 0, c_strm->si, &c_strm->dec); + if (status != PJ_SUCCESS) + goto err_cleanup; + + /* Create encoder channel: */ + buf_size = BUFFER_SIZE; + status = create_channel( pool, c_strm, PJMEDIA_DIR_ENCODING, + info->tx_pt, buf_size, c_strm->si, &c_strm->enc); + if (status != PJ_SUCCESS) + goto err_cleanup; + + /* Init RTCP session: */ + { + pjmedia_rtcp_session_setting rtcp_setting; + + pjmedia_rtcp_session_setting_default(&rtcp_setting); + rtcp_setting.name = c_strm->port.info.name.ptr; + rtcp_setting.ssrc = info->ssrc; + rtcp_setting.rtp_ts_base = pj_ntohl(c_strm->enc->rtp.out_hdr.ts); + rtcp_setting.clock_rate = info->fmt.clock_rate; + rtcp_setting.samples_per_frame = 1; + + pjmedia_rtcp_init2(&c_strm->rtcp, &rtcp_setting); + + if (info->rtp_seq_ts_set) { + c_strm->rtcp.stat.rtp_tx_last_seq = info->rtp_seq; + c_strm->rtcp.stat.rtp_tx_last_ts = info->rtp_ts; + } + + /* Subscribe to RTCP events */ + // Currently not needed, perhaps for future use. + //pjmedia_event_subscribe(NULL, &stream_event_cb, stream, + // &c_strm->rtcp); + } + + /* Allocate outgoing RTCP buffer, should be enough to hold SR/RR, SDES, + * BYE, and XR. + */ + c_strm->out_rtcp_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) + + sizeof(pjmedia_rtcp_common) + + (4 + (unsigned)c_strm->cname.slen) + + 32; + + if (c_strm->out_rtcp_pkt_size > PJMEDIA_MAX_MTU) + c_strm->out_rtcp_pkt_size = PJMEDIA_MAX_MTU; + + c_strm->out_rtcp_pkt = pj_pool_alloc(pool, c_strm->out_rtcp_pkt_size); + pj_bzero(&att_param, sizeof(att_param)); + att_param.stream = stream; + att_param.media_type = PJMEDIA_TYPE_TEXT; + att_param.user_data = stream; + + pj_sockaddr_cp(&att_param.rem_addr, &info->rem_addr); + pj_sockaddr_cp(&c_strm->rem_rtp_addr, &info->rem_addr); + if (stream->si.rtcp_mux) { + pj_sockaddr_cp(&att_param.rem_rtcp, &info->rem_addr); + } else if (pj_sockaddr_has_addr(&info->rem_rtcp)) { + pj_sockaddr_cp(&att_param.rem_rtcp, &info->rem_rtcp); + } + att_param.addr_len = pj_sockaddr_get_len(&info->rem_addr); + att_param.rtp_cb2 = &on_rx_rtp; + att_param.rtcp_cb = &on_rx_rtcp; + + /* Create group lock & attach handler */ + status = pj_grp_lock_create_w_handler(pool, NULL, stream, + &on_destroy, + &c_strm->grp_lock); + if (status != PJ_SUCCESS) + goto err_cleanup; + + /* Add ref */ + pj_grp_lock_add_ref(c_strm->grp_lock); + c_strm->port.grp_lock = c_strm->grp_lock; + + /* Only attach transport when stream is ready. */ + status = pjmedia_transport_attach2(tp, &att_param); + if (status != PJ_SUCCESS) + goto err_cleanup; + + /* Also add ref the transport group lock */ + c_strm->transport = tp; + if (c_strm->transport->grp_lock) + pj_grp_lock_add_ref(c_strm->transport->grp_lock); + + /* Send RTCP SDES */ + if (!c_strm->rtcp_sdes_bye_disabled) { + pjmedia_stream_common_send_rtcp_sdes(c_strm); + } + +#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 + /* NAT hole punching by sending KA packet via RTP transport. */ + if (c_strm->use_ka) + send_keep_alive_packet(c_strm); +#endif + + /* Success! */ + *p_stream = stream; + + PJ_LOG(3, (THIS_FILE, "Text stream %s created", c_strm->port.info.name.ptr)); + + return PJ_SUCCESS; + +err_cleanup: + pjmedia_txt_stream_destroy(stream); + return status; +} + +PJ_DEF(pj_status_t) pjmedia_txt_stream_start(pjmedia_txt_stream *stream) +{ + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; + pj_status_t status; + + pjmedia_stream_common_start(c_strm); + if (!c_strm->enc->paused || !c_strm->dec->paused) { + status = pjmedia_clock_start(stream->clock); + } else { + status = pjmedia_clock_stop(stream->clock); + } + + return status; +} + +PJ_DEF(pj_status_t) +pjmedia_txt_stream_get_info(const pjmedia_txt_stream *stream, + pjmedia_txt_stream_info *info) +{ + PJ_ASSERT_RETURN(stream && info, PJ_EINVAL); + + pj_memcpy(info, &stream->si, sizeof(pjmedia_txt_stream_info)); + return PJ_SUCCESS; +} + +static void call_cb(pjmedia_txt_stream *stream, pj_bool_t now) +{ + pjmedia_stream_common *c_strm = &stream->base; + char frm_type; + int next_seq; + + pj_mutex_lock(c_strm->jb_mutex); + + stream->is_waiting = PJ_FALSE; + do { + char frm_buf[BUFFER_SIZE]; + pj_size_t frm_size = sizeof(frm_buf); + pj_uint32_t ts; + + /* Check if we have a packet with the next sequence number. */ + pjmedia_jbuf_peek_frame(c_strm->jb, 0, NULL, NULL, &frm_type, NULL, + NULL, &next_seq); + + /* Jitter buffer is empty, just return. */ + if (frm_type == PJMEDIA_JB_ZERO_EMPTY_FRAME) + break; + + if (!now && stream->rx_last_seq != -1 && + next_seq != (pj_uint16_t)(stream->rx_last_seq + 1)) + { + /* If analysis of a received packet reveals a gap in the sequence, + * we need to wait for the missing packet(s) to arrive. + * The waiting time is determined by MAX_RX_WAITING_TIME, + */ + stream->is_waiting = PJ_TRUE; + pj_get_timestamp(&stream->rx_wait_ts); + break; + } + + pjmedia_jbuf_get_frame3(c_strm->jb, frm_buf, &frm_size, &frm_type, + NULL, &ts, &stream->rx_last_seq); + + /* Call callback. */ + pj_mutex_unlock(c_strm->jb_mutex); + if (stream->cb) { + pjmedia_txt_stream_data data; + + data.seq = next_seq; + data.ts = ts; + data.text.ptr = frm_buf; + data.text.slen = frm_size; + (*stream->cb)(stream, stream->cb_user_data, &data); + } else { + TRACE_((c_strm->port.info.name.ptr, "Text data %zu: %.*s", + frm_size, (int)frm_size, frm_buf)); + } + pj_mutex_lock(c_strm->jb_mutex); + } while (1); + + pj_mutex_unlock(c_strm->jb_mutex); +} + +static pj_status_t decode_red(pjmedia_txt_stream *stream, + unsigned pt, int seq, + const char *buf, unsigned buflen, + unsigned *red_len) +{ + pjmedia_stream_common *c_strm = &stream->base; + pjmedia_rtp_add_hdr hdr[NUM_BUFFERS]; + int i, len = 0, level = 0; + /* Acceptable seq delta for redundancy packets. + * The value is the same as the one used in rtp and jbuf + * for determining late packets. + */ + enum { MAX_DELTA = 100 }; + + /* Read the headers. */ + do { + pjmedia_rtp_add_hdr_short shdr; + + if (len + sizeof(shdr) > buflen) + return PJ_ETOOBIG; + + pj_memcpy(&shdr, buf, sizeof(shdr)); + if (shdr.pt != pt) { + /* Bad PT, the PT is different from the expected media PT. */ + return PJMEDIA_EINVALIDPT; + } + if (shdr.f == 0) { + /* Zero F bit indicating final block. */ + buf += sizeof(shdr); + len += sizeof(shdr); + break; + } + + /* Redundancy level higher than what we can support. */ + if (level >= c_strm->si->rx_red_level || level >= NUM_BUFFERS) + return PJ_ETOOBIG; + if (len + sizeof(hdr[level]) > buflen) + return PJ_ETOOBIG; + + /* Store the headers. */ + pj_memcpy(&hdr[level], buf, sizeof(hdr[level])); + hdr[level] = pj_ntohl(hdr[level]); + buf += sizeof(hdr[level]); + len += sizeof(hdr[level]); + + level++; + } while (1); + + /* Read the redundant data. */ + for (i = 0; i < level; i++) { + /* Bit 22-31 is block length. */ + unsigned length = (hdr[i] & 0x3FF); + int ext_seq; + pj_uint16_t delta; + + if (length == 0) + continue; + if (len + length > buflen) + return PJ_ETOOBIG; + + ext_seq = (pj_uint16_t)(seq + i - level); + TRACE_((c_strm->port.info.name.ptr, "Received RTP red, seq: %d, " + "%.*s (%d bytes)", ext_seq, (int)length, buf, length)); + + delta = (pj_uint16_t) (stream->rx_last_seq - ext_seq); + if (delta < MAX_DELTA) { + /* Redundant packets that we already have in jbuf, do nothing. */ + } else { + pjmedia_jbuf_put_frame(c_strm->jb, buf, length, ext_seq); + } + + buf += length; + len += length; + } + + *red_len = len; + + return PJ_SUCCESS; +} + +static pj_status_t on_stream_rx_rtp(pjmedia_stream_common *c_strm, + const pjmedia_rtp_hdr *hdr, + const void *payload, + unsigned payloadlen, + pjmedia_rtp_status seq_st, + pj_bool_t *pkt_discarded) +{ + pjmedia_txt_stream *stream = (pjmedia_txt_stream *)c_strm; + unsigned red_len = 0; + int seq; + pj_status_t status; + + pj_mutex_lock(c_strm->jb_mutex); + + if (seq_st.status.flag.restart) { + stream->rx_last_seq = -1; + pjmedia_jbuf_reset(c_strm->jb); + PJ_LOG(4, (c_strm->port.info.name.ptr, "Jitter buffer reset")); + } + + seq = pj_ntohs(hdr->seq); + + if (hdr->pt == c_strm->si->rx_red_pt) { + status = decode_red(stream, c_strm->si->rx_pt, seq, + (const char *)payload, payloadlen, &red_len); + if (status != PJ_SUCCESS) { + *pkt_discarded = PJ_TRUE; + pj_mutex_unlock(c_strm->jb_mutex); + return status; + } + } + + /* Put the primary data into jbuf. */ + payload = ((pj_uint8_t*)payload) + red_len; + payloadlen -= red_len; + if (seq > stream->rx_last_seq) { + pjmedia_jbuf_put_frame(c_strm->jb, payload, payloadlen, seq); + } + + pj_mutex_unlock(c_strm->jb_mutex); + + /* Check if we need to call the callback. */ + call_cb(stream, PJ_FALSE); + + return PJ_SUCCESS; +} + +static pj_status_t encode_red(unsigned level, unsigned pt, + red_buf *rbuf, unsigned rbuf_idx, + char *buf, int *size) +{ + int i; + unsigned len = 0; + + /* Encode RTP additional headers for redundancy. */ + for (i = level; i > 0; i--) { + pjmedia_rtp_add_hdr_short shdr; + pjmedia_rtp_add_hdr hdr = 0; + int past_idx, offset; + + past_idx = (int)rbuf_idx - i; + if (past_idx < 0) past_idx += NUM_BUFFERS; + + /* 1 means not final. */ + shdr.f = 1; + shdr.pt = (pj_uint16_t)pt; + + /* Timestamp is an offset, not absolute. */ + offset = rbuf[rbuf_idx].timestamp - rbuf[past_idx].timestamp; + /* 4.1. Redundant data with timestamp offset > 16383 MUST NOT + * be included. + */ + if (offset > 16383) { + offset = 0; + rbuf[past_idx].length = 0; + } + hdr = offset << 10; + + /* Block length. */ + hdr |= rbuf[past_idx].length; + + if (len + sizeof(hdr) > (unsigned)*size) + return PJ_ETOOBIG; + hdr = pj_htonl(hdr); + pj_memcpy(buf, &hdr, sizeof(hdr)); + pj_memcpy(buf, &shdr, sizeof(shdr)); + buf += sizeof(hdr); + len += sizeof(hdr); + } + + if (level > 0) { + pjmedia_rtp_add_hdr_short hdr; + + /* Last RTP additional header, for the primary data. */ + hdr.f = 0; + hdr.pt = (pj_uint16_t)pt; + if (len + sizeof(hdr) > (unsigned)*size) + return PJ_ETOOBIG; + pj_memcpy(buf, &hdr, sizeof(hdr)); + buf += sizeof(hdr); + len += sizeof(hdr); + } + + /* Encode the redundant data placed in age order (with the most + * recent put last), and finally, the primary data. + */ + for (i = level; i >= 0; i--) { + int idx = (int)rbuf_idx - i; + if (idx < 0) idx += NUM_BUFFERS; + + if (rbuf[idx].length == 0) + continue; + if (len + rbuf[idx].length > (unsigned)*size) + return PJ_ETOOBIG; + pj_memcpy(buf, rbuf[idx].buf, rbuf[idx].length); + buf += rbuf[idx].length; + len += rbuf[idx].length; + } + + *size = len; + return PJ_SUCCESS; +} + + +static pj_status_t send_text(pjmedia_txt_stream *stream, + unsigned rtp_ts_len) +{ + pjmedia_stream_common *c_strm = &stream->base; + pjmedia_channel *channel = c_strm->enc; + void *rtphdr; + int size; + unsigned pt = 0; + pj_status_t status = PJ_SUCCESS; + + pj_mutex_lock(c_strm->jb_mutex); + + if (stream->is_idle && + stream->tx_buf[stream->tx_buf_idx].length == 0) + { + /* Nothing to send. */ + goto on_return; + } + + pt = (stream->si.tx_red_pt && stream->tx_nred)? + stream->si.tx_red_pt: channel->pt; + + status = pjmedia_rtp_encode_rtp( &channel->rtp, + pt, 0, + (int)channel->buf_size, rtp_ts_len, + (const void**)&rtphdr, + &size); + + TRACE_((c_strm->port.info.name.ptr, "Sending text with seq %d, %.*s " + "(%d bytes), pt:%d red:%d", + pj_ntohs(channel->rtp.out_hdr.seq), + (int)stream->tx_buf[stream->tx_buf_idx].length, + stream->tx_buf[stream->tx_buf_idx].buf, + (int)stream->tx_buf[stream->tx_buf_idx].length, + pt, stream->tx_nred)); + + if (status != PJ_SUCCESS) + goto on_return; + + /* Copy RTP header to the beginning of packet */ + pj_memcpy(channel->buf, rtphdr, sizeof(pjmedia_rtp_hdr)); + + if (stream->is_idle) { + stream->is_idle = PJ_FALSE; + /* Set M-bit for the first packet in a session, and the first + * packet after an idle period. + */ + ((pjmedia_rtp_hdr *)channel->buf)->m = 1; + } + + size = channel->buf_size - sizeof(pjmedia_rtp_hdr); + status = encode_red(stream->tx_nred, channel->pt, + stream->tx_buf, stream->tx_buf_idx, + ((char*)channel->buf) + sizeof(pjmedia_rtp_hdr), + &size); + if (status != PJ_SUCCESS) + goto on_return; + + if (stream->tx_buf[stream->tx_buf_idx].length == 0) { + int i; + + /* We are in idle if: + * - no new T.140 data is available for transmission, and + * - there's no more redundancy packet to send. + */ + stream->is_idle = PJ_TRUE; + for (i = 0; i < stream->tx_nred - 1; i++) { + int idx = (int)stream->tx_buf_idx - 1 - i; + if (idx < 0) idx += NUM_BUFFERS; + if (stream->tx_buf[idx].length != 0) { + stream->is_idle = PJ_FALSE; + break; + } + } + + /* If idle, reset redundancy. */ + if (stream->is_idle) stream->tx_nred = 0; + } else if (stream->tx_nred < stream->si.tx_red_level) { + /* We have data, increase the number of redundancies. */ + stream->tx_nred++; + } + + stream->tx_buf_idx = (stream->tx_buf_idx + 1) % NUM_BUFFERS; + stream->tx_buf[stream->tx_buf_idx].length = 0; + + pj_mutex_unlock(c_strm->jb_mutex); + + /* Send the RTP packet to the transport. */ + status = pjmedia_transport_send_rtp(c_strm->transport, channel->buf, + size + sizeof(pjmedia_rtp_hdr)); + + pj_mutex_lock(c_strm->jb_mutex); + if (status == PJ_SUCCESS) { + pj_get_timestamp(&stream->tx_last_ts); + + /* Update stat */ + pjmedia_rtcp_tx_rtp(&c_strm->rtcp, (unsigned)size); + c_strm->rtcp.stat.rtp_tx_last_ts = + pj_ntohl(c_strm->enc->rtp.out_hdr.ts); + c_strm->rtcp.stat.rtp_tx_last_seq = + pj_ntohs(c_strm->enc->rtp.out_hdr.seq); + +#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 + /* Update time of last sending packet. */ + pj_gettimeofday(&c_strm->last_frm_ts_sent); +#endif + + } else { + TRACE_((c_strm->port.info.name.ptr, "Failed sending text %d", status)); + } + +on_return: + pj_mutex_unlock(c_strm->jb_mutex); + return status; +} + +/** + * check_tx_rtcp() + * This function is for transmitting periodic RTCP SR/RR report. + */ +static void check_tx_rtcp(pjmedia_txt_stream *stream) +{ + pjmedia_stream_common *c_strm = &stream->base; + pj_timestamp now; + + pj_get_timestamp(&now); + if (stream->rtcp_last_tx.u64 == 0) { + stream->rtcp_last_tx = now; + } else if (pj_elapsed_msec(&stream->rtcp_last_tx, &now) >= + c_strm->rtcp_interval) + { + pj_status_t status; + + status = send_rtcp(c_strm, !c_strm->rtcp_sdes_bye_disabled, PJ_FALSE, + PJ_FALSE, PJ_FALSE, PJ_FALSE, PJ_FALSE); + if (status == PJ_SUCCESS) { + stream->rtcp_last_tx = now; + } + } +} + +/* Clock callback */ +static void clock_cb(const pj_timestamp *ts, void *user_data) +{ + pjmedia_txt_stream *stream = (pjmedia_txt_stream *)user_data; + pj_timestamp now; + unsigned interval; + + PJ_UNUSED_ARG(ts); + + pj_get_timestamp(&now); + interval = pj_elapsed_msec(&stream->tx_last_ts, &now); + /* If the interval is at least the buffering time, or + * if the next clock callback occurs past the maximum buffering + * time, send the text now. + */ + if (interval >= stream->buf_time || + interval + stream->buf_time > MAX_BUFFERING_TIME) + { + send_text(stream, interval); + } + + /* If we are waiting for the packet gap, and the next clock cb + * occurs past the maximum waiting time, call the rx callback + * now. + */ + if (stream->is_waiting) { + interval = pj_elapsed_msec(&stream->rx_wait_ts, &now); + if (interval + stream->buf_time > MAX_RX_WAITING_TIME) { + call_cb(stream, PJ_TRUE); + } + } + + /* Check if now is the time to transmit RTCP SR/RR report. */ + check_tx_rtcp(stream); +} + +PJ_DEF(pj_status_t) +pjmedia_txt_stream_send_text(pjmedia_txt_stream *stream, const pj_str_t *text) +{ + pjmedia_stream_common *c_strm = &stream->base; + pj_timestamp now; + unsigned interval; + pj_status_t status = PJ_SUCCESS; + + PJ_ASSERT_RETURN(stream, PJ_EINVAL); + + pj_mutex_lock(c_strm->jb_mutex); + + if (stream->tx_buf[stream->tx_buf_idx].length + text->slen > BUFFER_SIZE) + { + pj_mutex_unlock(c_strm->jb_mutex); + return PJ_ETOOMANY; + } + + pj_memcpy(stream->tx_buf[stream->tx_buf_idx].buf + + stream->tx_buf[stream->tx_buf_idx].length, + text->ptr, text->slen); + stream->tx_buf[stream->tx_buf_idx].length += text->slen; + + /* Decide if we should send the text immediately or just buffer it. */ + pj_get_timestamp(&now); + interval = pj_elapsed_msec(&stream->tx_last_ts, &now); + if (interval >= stream->buf_time) { + status = send_text(stream, interval); + } + + pj_mutex_unlock(c_strm->jb_mutex); + + return status; +} + +PJ_DEF(pj_status_t) pjmedia_txt_stream_set_rx_callback( + pjmedia_txt_stream *stream, + void (*cb)(pjmedia_txt_stream *, + void *user_data, + const pjmedia_txt_stream_data *data), + void *user_data, + unsigned option) +{ + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; + + PJ_ASSERT_RETURN(stream, PJ_EINVAL); + + PJ_UNUSED_ARG(option); + + pj_mutex_lock(c_strm->jb_mutex); + + stream->cb = cb; + stream->cb_user_data = user_data; + + pj_mutex_unlock(c_strm->jb_mutex); + + return PJ_SUCCESS; +} + +/* + * Internal function for collecting codec info from the SDP media. + */ +static pj_status_t get_codec_info(pjmedia_txt_stream_info *si, + pj_pool_t *pool, + const pjmedia_sdp_media *local_m, + const pjmedia_sdp_media *rem_m) +{ + static const pj_str_t ID_RTPMAP = { "rtpmap", 6 }; + static const pj_str_t ID_REDUNDANCY = { "red", 3 }; + const pjmedia_sdp_attr *attr; + pjmedia_sdp_rtpmap *rtpmap; + unsigned i, fmti, pt = 0; + pj_status_t status = PJ_SUCCESS; + + /* Find the first pt which is not redundancy */ + for (fmti = 0; fmti < local_m->desc.fmt_count; ++fmti) { + pjmedia_sdp_rtpmap r; + + if (!pj_isdigit(*local_m->desc.fmt[fmti].ptr)) + return PJMEDIA_EINVALIDPT; + pt = pj_strtoul(&local_m->desc.fmt[fmti]); + + attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, + &local_m->desc.fmt[fmti]); + if (attr == NULL) + continue; + + status = pjmedia_sdp_attr_get_rtpmap(attr, &r); + if (status != PJ_SUCCESS) + continue; + + if (pj_strcmp(&r.enc_name, &ID_REDUNDANCY) != 0) + break; + } + if (fmti >= local_m->desc.fmt_count) + return PJMEDIA_EINVALIDPT; + + pt = pj_strtoul(&local_m->desc.fmt[fmti]); + if (pt < 96) + return PJMEDIA_EINVALIDPT; + + /* Get payload type for receiving direction */ + si->rx_pt = pt; + + /* Get codec info. */ + attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, + &local_m->desc.fmt[fmti]); + if (attr == NULL) + return PJMEDIA_EMISSINGRTPMAP; + + status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); + if (status != PJ_SUCCESS) + return status; + + /* Build codec format info: */ + si->fmt.type = si->type; + si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); + si->fmt.encoding_name = rtpmap->enc_name; + si->fmt.clock_rate = rtpmap->clock_rate; + si->fmt.channel_cnt = 1; + + /* Determine payload type for outgoing channel, by finding + * dynamic payload type in remote SDP that matches the answer. + */ + si->tx_pt = 0xFFFF; + for (i=0; idesc.fmt_count; ++i) { + if (pjmedia_sdp_neg_fmt_match(pool, + (pjmedia_sdp_media*)local_m, fmti, + (pjmedia_sdp_media*)rem_m, i, fmti) == + PJ_SUCCESS) + { + /* Found matched codec. */ + si->tx_pt = pj_strtoul(&rem_m->desc.fmt[i]); + break; + } + } + + if (si->tx_pt == 0xFFFF) + return PJMEDIA_EMISSINGRTPMAP; + + /* Get remote fmtp for our encoder. */ + pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt, + &si->enc_fmtp); + + /* Get local fmtp for our decoder. */ + pjmedia_stream_info_parse_fmtp(pool, local_m, si->rx_pt, + &si->dec_fmtp); + + + if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE) + return status; + + return PJ_SUCCESS; +} + +/* + * Create stream info from SDP media line. + */ +PJ_DEF(pj_status_t) pjmedia_txt_stream_info_from_sdp( + pjmedia_txt_stream_info *si, + pj_pool_t *pool, + pjmedia_endpt *endpt, + const pjmedia_sdp_session *local, + const pjmedia_sdp_session *remote, + unsigned stream_idx) +{ + pjmedia_stream_info_common *csi = (pjmedia_stream_info_common *)si; + const pjmedia_sdp_media *local_m; + const pjmedia_sdp_media *rem_m; + pj_bool_t active; + pj_status_t status; + + PJ_ASSERT_RETURN(si, PJ_EINVAL); + + pj_bzero(si, sizeof(*si)); + status = pjmedia_stream_info_common_from_sdp(csi, pool, endpt, local, + remote, stream_idx, &active); + if (status != PJ_SUCCESS || !active) + return status; + + /* Keep SDP shortcuts */ + local_m = local->media[stream_idx]; + rem_m = remote->media[stream_idx]; + + /* Media type must be text */ + if (pjmedia_get_type(&local_m->desc.media) != PJMEDIA_TYPE_TEXT) + return PJMEDIA_EINVALIMEDIATYPE; + + /* Get codec info */ + status = get_codec_info(si, pool, local_m, rem_m); + + /* Get redundancy info */ + pjmedia_stream_info_common_parse_redundancy(csi, pool, local_m, rem_m); + + return status; +} diff --git a/pjmedia/src/pjmedia/types.c b/pjmedia/src/pjmedia/types.c index 6712eb71a9..99d40fff4f 100644 --- a/pjmedia/src/pjmedia/types.c +++ b/pjmedia/src/pjmedia/types.c @@ -32,6 +32,7 @@ static pjmedia_type_map media_type_names[] = { {PJMEDIA_TYPE_NONE, "none"}, {PJMEDIA_TYPE_AUDIO, "audio"}, {PJMEDIA_TYPE_VIDEO, "video"}, + {PJMEDIA_TYPE_TEXT, "text"}, {PJMEDIA_TYPE_APPLICATION, "application"}, {PJMEDIA_TYPE_UNKNOWN, "unknown"} }; @@ -42,7 +43,7 @@ static pjmedia_type_map media_type_names[] = { PJ_DEF(const char*) pjmedia_type_name(pjmedia_type t) { pj_assert(t < (int)PJ_ARRAY_SIZE(media_type_names)); - pj_assert(PJMEDIA_TYPE_UNKNOWN == 4); + pj_assert(PJMEDIA_TYPE_UNKNOWN == 5); if (t < (int)PJ_ARRAY_SIZE(media_type_names)) return media_type_names[t].name; diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c index f8479f5c59..b97c4fcd15 100644 --- a/pjmedia/src/pjmedia/vid_stream.c +++ b/pjmedia/src/pjmedia/vid_stream.c @@ -1305,18 +1305,19 @@ static pj_status_t get_frame(pjmedia_port *port, /* * Create media channel. */ -static pj_status_t create_channel( pj_pool_t *pool, - pjmedia_vid_stream *stream, - pjmedia_dir dir, - unsigned pt, - const pjmedia_vid_stream_info *info, - pjmedia_vid_channel **p_channel) +static pj_status_t create_vid_channel( pj_pool_t *pool, + pjmedia_vid_stream *stream, + pjmedia_dir dir, + unsigned pt, + const pjmedia_vid_stream_info *info, + pjmedia_vid_channel **p_channel) { enum { M = 32 }; pjmedia_stream_common *c_strm = &stream->base; pjmedia_vid_channel *channel; pj_status_t status; unsigned min_out_pkt_size; + unsigned buf_size = 0; pj_str_t name; const char *type_name; pjmedia_format *fmt; @@ -1326,31 +1327,9 @@ static pj_status_t create_channel( pj_pool_t *pool, pj_assert(info->type == PJMEDIA_TYPE_VIDEO); pj_assert(dir == PJMEDIA_DIR_DECODING || dir == PJMEDIA_DIR_ENCODING); - /* Allocate memory for channel descriptor */ - channel = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_channel); - PJ_ASSERT_RETURN(channel != NULL, PJ_ENOMEM); - - /* Init vars */ - if (dir==PJMEDIA_DIR_DECODING) { - type_name = "vstdec"; - fmt = &info->codec_param->dec_fmt; - } else { - type_name = "vstenc"; - fmt = &info->codec_param->enc_fmt; - } - name.ptr = (char*) pj_pool_alloc(pool, M); - name.slen = pj_ansi_snprintf(name.ptr, M, "%s%p", type_name, stream); - pi = &channel->port.info; - - /* Init channel info. */ - channel->stream = c_strm; - channel->dir = dir; - channel->paused = 1; - channel->pt = pt; - /* Allocate buffer for outgoing packet. */ if (dir == PJMEDIA_DIR_ENCODING) { - channel->buf_size = sizeof(pjmedia_rtp_hdr) + c_strm->frame_size; + buf_size = sizeof(pjmedia_rtp_hdr) + c_strm->frame_size; /* It should big enough to hold (minimally) RTCP SR with an SDES. */ min_out_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) + @@ -1358,29 +1337,28 @@ static pj_status_t create_channel( pj_pool_t *pool, (4 + (unsigned)c_strm->cname.slen) + 32; - if (channel->buf_size < min_out_pkt_size) - channel->buf_size = min_out_pkt_size; - - channel->buf = pj_pool_alloc(pool, channel->buf_size); - PJ_ASSERT_RETURN(channel->buf != NULL, PJ_ENOMEM); + if (buf_size < min_out_pkt_size) + buf_size = min_out_pkt_size; } - /* Create RTP and RTCP sessions: */ - { - pjmedia_rtp_session_setting settings; - - settings.flags = (pj_uint8_t)((info->rtp_seq_ts_set << 2) | - (info->has_rem_ssrc << 4) | 3); - settings.default_pt = pt; - settings.sender_ssrc = info->ssrc; - settings.peer_ssrc = info->rem_ssrc; - settings.seq = info->rtp_seq; - settings.ts = info->rtp_ts; - status = pjmedia_rtp_session_init2(&channel->rtp, settings); - } + status = create_channel(pool, c_strm, dir, pt, buf_size, + (pjmedia_stream_info_common *)info, + &channel); if (status != PJ_SUCCESS) return status; + /* Init vars */ + if (dir==PJMEDIA_DIR_DECODING) { + type_name = "vstdec"; + fmt = &info->codec_param->dec_fmt; + } else { + type_name = "vstenc"; + fmt = &info->codec_param->enc_fmt; + } + name.ptr = (char*) pj_pool_alloc(pool, M); + name.slen = pj_ansi_snprintf(name.ptr, M, "%s%p", type_name, stream); + pi = &channel->port.info; + /* Init port. */ pjmedia_port_info_init2(pi, &name, SIGNATURE, dir, fmt); if (dir == PJMEDIA_DIR_DECODING) { @@ -1600,13 +1578,13 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( stream->dec_max_fps = vfd_dec->fps; /* Create decoder channel */ - status = create_channel( pool, stream, PJMEDIA_DIR_DECODING, + status = create_vid_channel( pool, stream, PJMEDIA_DIR_DECODING, info->rx_pt, info, &c_strm->dec); if (status != PJ_SUCCESS) goto err_cleanup; /* Create encoder channel */ - status = create_channel( pool, stream, PJMEDIA_DIR_ENCODING, + status = create_vid_channel( pool, stream, PJMEDIA_DIR_ENCODING, info->tx_pt, info, &c_strm->enc); if (status != PJ_SUCCESS) goto err_cleanup; @@ -2022,8 +2000,8 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_stat_jbuf( const pjmedia_vid_stream *stream, pjmedia_jb_state *state) { - PJ_ASSERT_RETURN(stream && state, PJ_EINVAL); - return pjmedia_jbuf_get_state(stream->base.jb, state); + return pjmedia_stream_common_get_stat_jbuf((pjmedia_stream_common *)stream, + state); } @@ -2045,27 +2023,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_info( */ PJ_DEF(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream) { - pjmedia_stream_common *c_strm = (pjmedia_stream_common *)stream; - - PJ_ASSERT_RETURN(stream && c_strm->enc && c_strm->dec, PJ_EINVALIDOP); - - if (c_strm->enc && (c_strm->dir & PJMEDIA_DIR_ENCODING)) { - c_strm->enc->paused = 0; - //pjmedia_snd_stream_start(c_strm->enc->snd_stream); - PJ_LOG(4,(c_strm->enc->port.info.name.ptr, "Encoder stream started")); - } else { - PJ_LOG(4,(c_strm->enc->port.info.name.ptr, "Encoder stream paused")); - } - - if (c_strm->dec && (c_strm->dir & PJMEDIA_DIR_DECODING)) { - c_strm->dec->paused = 0; - //pjmedia_snd_stream_start(c_strm->dec->snd_stream); - PJ_LOG(4,(c_strm->dec->port.info.name.ptr, "Decoder stream started")); - } else { - PJ_LOG(4,(c_strm->dec->port.info.name.ptr, "Decoder stream paused")); - } - - return PJ_SUCCESS; + return pjmedia_stream_common_start((pjmedia_stream_common *) stream); } diff --git a/pjsip-apps/src/3rdparty_media_sample/Makefile b/pjsip-apps/src/3rdparty_media_sample/Makefile index 3fb635a224..245b8b0120 100644 --- a/pjsip-apps/src/3rdparty_media_sample/Makefile +++ b/pjsip-apps/src/3rdparty_media_sample/Makefile @@ -9,7 +9,7 @@ export _CFLAGS := $(PJ_CFLAGS) $(CFLAGS) export _CXXFLAGS:= $(PJ_CXXFLAGS) export _LDFLAGS := $(PJ_LDFLAGS) $(PJ_LDLIBS) $(LDFLAGS) -OBJS = alt_pjsua_aud.o alt_pjsua_vid.o pjsua_app.o main.o pjsua_app_cli.o pjsua_app_common.o pjsua_app_config.o pjsua_app_legacy.o +OBJS = alt_pjsua_aud.o alt_pjsua_txt.o alt_pjsua_vid.o pjsua_app.o main.o pjsua_app_cli.o pjsua_app_common.o pjsua_app_config.o pjsua_app_legacy.o all: alt_pjsua diff --git a/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_aud.c b/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_aud.c index 180211dd6d..75b65f86b7 100644 --- a/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_aud.c +++ b/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_aud.c @@ -366,6 +366,14 @@ PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id, return PJ_ENOTSUP; } +PJ_DEF(pj_status_t) pjsua_call_dial_dtmf2( pjsua_call_id call_id, + const pj_str_t *digits, + unsigned duration) +{ + UNIMPLEMENTED(pjsua_call_dial_dtmf2) + return PJ_ENOTSUP; +} + /***************************************************************************** * Below are auxiliary API that we don't support (feel free to implement them * with the other media stack) diff --git a/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_txt.c b/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_txt.c new file mode 100644 index 0000000000..6af2a9069b --- /dev/null +++ b/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_txt.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2024-2025 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include + +#if defined(PJSUA_MEDIA_HAS_PJMEDIA) && PJSUA_MEDIA_HAS_PJMEDIA != 0 +# error The PJSUA_MEDIA_HAS_PJMEDIA should be declared as zero +#endif + + +#define THIS_FILE "alt_pjsua_txt.c" +#define UNIMPLEMENTED(func) PJ_LOG(2,(THIS_FILE, "*** Call to unimplemented function %s ***", #func)); + +/***************************************************************************** + * API + */ + +/* Our callback to receive incoming RTP packets */ +static void txt_rtp_cb(void *user_data, void *pkt, pj_ssize_t size) +{ + pjsua_call_media *call_med = (pjsua_call_media*) user_data; + + /* TODO: Do something with the packet */ + PJ_LOG(4,(THIS_FILE, "RX %d bytes text RTP packet", (int)size)); +} + +/* Our callback to receive RTCP packets */ +static void txt_rtcp_cb(void *user_data, void *pkt, pj_ssize_t size) +{ + pjsua_call_media *call_med = (pjsua_call_media*) user_data; + + /* TODO: Do something with the packet here */ + PJ_LOG(4,(THIS_FILE, "RX %d bytes text RTCP packet", (int)size)); +} + +/* A demo function to send dummy "RTP" packets periodically. You would not + * need to have this function in the real app! + */ +static void timer_to_send_txt_rtp(void *user_data) +{ + pjsua_call_media *call_med = (pjsua_call_media*) user_data; + const char *pkt = "Not RTP packet"; + + if (!call_med->call || !call_med->call->inv || !call_med->tp) { + /* Call has been disconnected. There is race condition here as + * this cb may be called sometime after call has been disconnected */ + return; + } + + pjmedia_transport_send_rtp(call_med->tp, pkt, strlen(pkt)); + + pjsua_schedule_timer2(&timer_to_send_txt_rtp, call_med, 2000); +} + +static void timer_to_send_txt_rtcp(void *user_data) +{ + pjsua_call_media *call_med = (pjsua_call_media*) user_data; + const char *pkt = "Not RTCP packet"; + + if (!call_med->call || !call_med->call->inv || !call_med->tp) { + /* Call has been disconnected. There is race condition here as + * this cb may be called sometime after call has been disconnected */ + return; + } + + pjmedia_transport_send_rtcp(call_med->tp, pkt, strlen(pkt)); + + pjsua_schedule_timer2(&timer_to_send_txt_rtcp, call_med, 5000); +} + +/* Stop the text stream of a call. */ +void pjsua_txt_stop_stream(pjsua_call_media *call_med) +{ + /* Detach our RTP/RTCP callbacks from transport */ + if (call_med->tp) { + pjmedia_transport_detach(call_med->tp, call_med); + } + + /* TODO: destroy your text stream here */ +} + +/* + * This function is called whenever SDP negotiation has completed + * successfully. Here you'd want to start your text stream + * based on the info in the SDPs. + */ +pj_status_t pjsua_txt_channel_update(pjsua_call_media *call_med, + pj_pool_t *tmp_pool, + pjmedia_txt_stream_info *si, + const pjmedia_sdp_session *local_sdp, + const pjmedia_sdp_session *remote_sdp) +{ + pj_status_t status = PJ_SUCCESS; + + PJ_LOG(4,(THIS_FILE,"Alt text channel update..")); + pj_log_push_indent(); + + /* Check if no media is active */ + if (si->dir != PJMEDIA_DIR_NONE) { + /* Attach our RTP and RTCP callbacks to the media transport */ + status = pjmedia_transport_attach(call_med->tp, call_med, + &si->rem_addr, &si->rem_rtcp, + pj_sockaddr_get_len(&si->rem_addr), + &txt_rtp_cb, &txt_rtcp_cb); + + /* For a demonstration, let's use a timer to send "RTP" packet + * periodically. + */ + pjsua_schedule_timer2(&timer_to_send_txt_rtp, call_med, 0); + pjsua_schedule_timer2(&timer_to_send_txt_rtcp, call_med, 2500); + + /* TODO: + * - Create and start your media stream based on the parameters + * in si + */ + } + +on_return: + pj_log_pop_indent(); + return status; +} + +/***************************************************************************** + * + * Call API which MAY need to be re-implemented if different backend is used. + */ + +/* Send text to remote. */ +PJ_DEF(pj_status_t) +pjsua_call_send_text(pjsua_call_id call_id, + const pjsua_call_send_text_param *param) +{ + UNIMPLEMENTED(pjsua_call_send_text) + return PJ_ENOTSUP; +} diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c index 26764ab0d5..7afca22d8e 100644 --- a/pjsip-apps/src/pjsua/pjsua_app.c +++ b/pjsip-apps/src/pjsua/pjsua_app.c @@ -325,6 +325,7 @@ static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsua_call_setting_default(&opt); opt.aud_cnt = app_config.aud_cnt; opt.vid_cnt = app_config.vid.vid_cnt; + opt.txt_cnt = app_config.txt_cnt; pjsua_call_answer2(call_id, &opt, app_config.auto_answer, NULL, NULL); @@ -345,7 +346,7 @@ static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, PJ_LOG(3,(THIS_FILE, "Incoming call for account %d!\n" - "Media count: %d audio & %d video\n" + "Media count: %d audio & %d video & %d text\n" "%s" "From: %.*s\n" "To: %.*s\n" @@ -353,6 +354,7 @@ static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, acc_id, call_info.rem_aud_cnt, call_info.rem_vid_cnt, + call_info.rem_txt_cnt, notif_st, (int)call_info.remote_info.slen, call_info.remote_info.ptr, @@ -588,6 +590,21 @@ static void call_on_dtmf_callback2(pjsua_call_id call_id, call_id, info->digit, duration, method)); } +/* Incoming text stream callback. */ +static void call_on_rx_text(pjsua_call_id call_id, + const pjsua_txt_stream_data *data) +{ + if (data->text.slen == 0) { + PJ_LOG(4, (THIS_FILE, "Received empty T140 block with seq %d", + data->seq)); + } else { + PJ_LOG(3, (THIS_FILE, "Incoming text on call %d, seq %d: %.*s " + "(%d bytes)", call_id, data->seq, + (int)data->text.slen, data->text.ptr, + (int)data->text.slen)); + } +} + /* * Redirection handler. */ @@ -1464,6 +1481,7 @@ static pj_status_t app_init(void) app_config.cfg.cb.on_call_media_state = &on_call_media_state; app_config.cfg.cb.on_incoming_call = &on_incoming_call; app_config.cfg.cb.on_dtmf_digit2 = &call_on_dtmf_callback2; + app_config.cfg.cb.on_call_rx_text = &call_on_rx_text; app_config.cfg.cb.on_call_redirected = &call_on_redirected; app_config.cfg.cb.on_reg_state = &on_reg_state; app_config.cfg.cb.on_incoming_subscribe = &on_incoming_subscribe; @@ -1759,6 +1777,7 @@ static pj_status_t app_init(void) pjsua_acc_get_config(aid, tmp_pool, &acc_cfg); app_config_init_video(&acc_cfg); + acc_cfg.txt_red_level = app_config.txt_red_level; acc_cfg.rtp_cfg = app_config.rtp_cfg; pjsua_acc_modify(aid, &acc_cfg); } @@ -1803,6 +1822,7 @@ static pj_status_t app_init(void) pjsua_acc_get_config(aid, tmp_pool, &acc_cfg); app_config_init_video(&acc_cfg); + acc_cfg.txt_red_level = app_config.txt_red_level; acc_cfg.rtp_cfg = app_config.rtp_cfg; // acc_cfg.ipv6_media_use = PJSUA_IPV6_ENABLED; pjsua_acc_modify(aid, &acc_cfg); @@ -1838,6 +1858,7 @@ static pj_status_t app_init(void) pjsua_acc_get_config(aid, tmp_pool, &acc_cfg); app_config_init_video(&acc_cfg); + acc_cfg.txt_red_level = app_config.txt_red_level; acc_cfg.rtp_cfg = app_config.rtp_cfg; pjsua_acc_modify(aid, &acc_cfg); } @@ -1868,6 +1889,7 @@ static pj_status_t app_init(void) pjsua_acc_get_config(aid, tmp_pool, &acc_cfg); app_config_init_video(&acc_cfg); + acc_cfg.txt_red_level = app_config.txt_red_level; acc_cfg.rtp_cfg = app_config.rtp_cfg; // acc_cfg.ipv6_media_use = PJSUA_IPV6_ENABLED; pjsua_acc_modify(aid, &acc_cfg); @@ -1907,6 +1929,7 @@ static pj_status_t app_init(void) pjsua_acc_get_config(acc_id, tmp_pool, &acc_cfg); app_config_init_video(&acc_cfg); + acc_cfg.txt_red_level = app_config.txt_red_level; acc_cfg.rtp_cfg = app_config.rtp_cfg; pjsua_acc_modify(acc_id, &acc_cfg); } @@ -1936,6 +1959,7 @@ static pj_status_t app_init(void) pjsua_acc_get_config(aid, tmp_pool, &acc_cfg); app_config_init_video(&acc_cfg); + acc_cfg.txt_red_level = app_config.txt_red_level; acc_cfg.rtp_cfg = app_config.rtp_cfg; // acc_cfg.ipv6_media_use = PJSUA_IPV6_ENABLED; pjsua_acc_modify(aid, &acc_cfg); @@ -1961,6 +1985,7 @@ static pj_status_t app_init(void) app_config.acc_cfg[i].reg_first_retry_interval = 60; app_config_init_video(&app_config.acc_cfg[i]); + app_config.acc_cfg[i].txt_red_level = app_config.txt_red_level; status = pjsua_acc_add(&app_config.acc_cfg[i], PJ_TRUE, NULL); if (status != PJ_SUCCESS) @@ -2019,6 +2044,7 @@ static pj_status_t app_init(void) pjsua_call_setting_default(&call_opt); call_opt.aud_cnt = app_config.aud_cnt; call_opt.vid_cnt = app_config.vid.vid_cnt; + call_opt.txt_cnt = app_config.txt_cnt; if (app_config.enable_loam) { call_opt.flag |= PJSUA_CALL_NO_SDP_OFFER; } @@ -2093,6 +2119,7 @@ pj_status_t pjsua_app_run(pj_bool_t wait_telnet_cli) pjsua_call_setting_default(&call_opt); call_opt.aud_cnt = app_config.aud_cnt; call_opt.vid_cnt = app_config.vid.vid_cnt; + call_opt.txt_cnt = app_config.txt_cnt; pjsua_call_make_call(current_acc, &uri_arg, &call_opt, NULL, NULL, NULL); diff --git a/pjsip-apps/src/pjsua/pjsua_app_cli.c b/pjsip-apps/src/pjsua/pjsua_app_cli.c index 44849230d5..42d65359d5 100644 --- a/pjsip-apps/src/pjsua/pjsua_app_cli.c +++ b/pjsip-apps/src/pjsua/pjsua_app_cli.c @@ -59,6 +59,7 @@ #define CMD_CALL_DUMP_Q ((CMD_CALL*10)+16) #define CMD_CALL_SEND_ARB ((CMD_CALL*10)+17) #define CMD_CALL_LIST ((CMD_CALL*10)+18) +#define CMD_CALL_RTT ((CMD_CALL*10)+19) /* im & presence level 2 command */ #define CMD_PRESENCE_ADD_BUDDY ((CMD_PRESENCE*10)+1) @@ -804,6 +805,7 @@ static pj_status_t cmd_add_account(pj_cli_cmd_val *cval) acc_cfg.cred_info[0].data = cval->argv[5]; acc_cfg.rtp_cfg = app_config.rtp_cfg; + acc_cfg.txt_red_level = app_config.txt_red_level; app_config_init_video(&acc_cfg); status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL); @@ -1943,6 +1945,31 @@ static pj_status_t cmd_dtmf_2833(pj_cli_cmd_val *cval) return PJ_SUCCESS; } +/* Send real-time text */ +static pj_status_t cmd_rtt(pj_cli_cmd_val *cval) +{ + if (current_call == PJSUA_INVALID_ID) { + + PJ_LOG(3,(THIS_FILE, "No current call")); + + } else { + pjsua_call_send_text_param param; + pj_status_t status; + + pjsua_call_send_text_param_default(¶m); + param.text = cval->argv[1]; + status = pjsua_call_send_text(current_call, ¶m); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to send text", status); + } else { + const pj_str_t msg = pj_str("Text enqueued " + "for transmission\n"); + pj_cli_sess_write_msg(cval->sess, msg.ptr, msg.slen); + } + } + return PJ_SUCCESS; +} + /* Send DTMF with SIP Info */ static pj_status_t cmd_call_info(pj_cli_cmd_val *cval) { @@ -2112,6 +2139,7 @@ pj_status_t cmd_call_handler(pj_cli_cmd_val *cval) pjsua_call_setting_default(&call_opt); call_opt.aud_cnt = app_config.aud_cnt; call_opt.vid_cnt = app_config.vid.vid_cnt; + call_opt.txt_cnt = app_config.txt_cnt; if (app_config.enable_loam) { call_opt.flag |= PJSUA_CALL_NO_SDP_OFFER; } @@ -2155,6 +2183,9 @@ pj_status_t cmd_call_handler(pj_cli_cmd_val *cval) case CMD_CALL_D2833: status = cmd_dtmf_2833(cval); break; + case CMD_CALL_RTT: + status = cmd_rtt(cval); + break; case CMD_CALL_INFO: status = cmd_call_info(cval); break; @@ -2897,6 +2928,10 @@ static pj_status_t add_call_command(pj_cli_t *c) " " "
" " " + " " + " " + " " ""; pj_str_t xml = pj_str(call_command); diff --git a/pjsip-apps/src/pjsua/pjsua_app_common.h b/pjsip-apps/src/pjsua/pjsua_app_common.h index 4ee56b9b00..b1fbf2e1db 100644 --- a/pjsip-apps/src/pjsua/pjsua_app_common.h +++ b/pjsip-apps/src/pjsua/pjsua_app_common.h @@ -145,6 +145,10 @@ typedef struct pjsua_app_config app_vid vid; unsigned aud_cnt; + /* Text setting */ + unsigned txt_cnt; + int txt_red_level; + /* AVI to play */ unsigned avi_cnt; struct { diff --git a/pjsip-apps/src/pjsua/pjsua_app_config.c b/pjsip-apps/src/pjsua/pjsua_app_config.c index 0743a0e247..5607ff7f93 100644 --- a/pjsip-apps/src/pjsua/pjsua_app_config.c +++ b/pjsip-apps/src/pjsua/pjsua_app_config.c @@ -186,6 +186,11 @@ static void usage(void) puts (" --auto-play-avi Automatically play the AVI media to call"); #endif + puts (""); + puts ("Text Options:"); + puts (" --text Enable real-time text stream"); + puts (" --text-red=N Text stream redundancy level (default=0)"); + puts (""); puts ("Media Transport Options:"); puts (" --use-ice Enable ICE (default:no)"); @@ -406,7 +411,7 @@ static pj_status_t parse_args(int argc, char *argv[], OPT_AUTO_UPDATE_NAT,OPT_USE_COMPACT_FORM,OPT_DIS_CODEC, OPT_DISABLE_STUN, OPT_NO_FORCE_LR, OPT_TIMER, OPT_TIMER_SE, OPT_TIMER_MIN_SE, - OPT_VIDEO, OPT_EXTRA_AUDIO, + OPT_VIDEO, OPT_TEXT, OPT_TEXT_RED, OPT_EXTRA_AUDIO, OPT_VCAPTURE_DEV, OPT_VRENDER_DEV, OPT_PLAY_AVI, OPT_AUTO_PLAY_AVI, OPT_USE_CLI, OPT_CLI_TELNET_PORT, OPT_DISABLE_CLI_CONSOLE }; @@ -550,6 +555,8 @@ static pj_status_t parse_args(int argc, char *argv[], { "timer-min-se", 1, 0, OPT_TIMER_MIN_SE}, { "outb-rid", 1, 0, OPT_OUTB_RID}, { "video", 0, 0, OPT_VIDEO}, + { "text", 0, 0, OPT_TEXT}, + { "text-red", 1, 0, OPT_TEXT_RED}, { "extra-audio",0, 0, OPT_EXTRA_AUDIO}, { "vcapture-dev", 1, 0, OPT_VCAPTURE_DEV}, { "vrender-dev", 1, 0, OPT_VRENDER_DEV}, @@ -1524,6 +1531,12 @@ static pj_status_t parse_args(int argc, char *argv[], cfg->vid.in_auto_show = PJ_TRUE; cfg->vid.out_auto_transmit = PJ_TRUE; break; + case OPT_TEXT: + cfg->txt_cnt = 1; + break; + case OPT_TEXT_RED: + cfg->txt_red_level = atoi(pj_optarg); + break; case OPT_EXTRA_AUDIO: cfg->aud_cnt++; break; @@ -2167,6 +2180,16 @@ int write_settings(pjsua_app_config *config, char *buf, pj_size_t max) pj_strcat2(&cfg, "--extra-audio\n"); } + /* Text */ + if (config->txt_cnt) { + pj_strcat2(&cfg, "--text\n"); + } + if (config->txt_red_level) { + pj_ansi_snprintf(line, sizeof(line), "--text-red %d\n", + (int)config->txt_red_level); + pj_strcat2(&cfg, line); + } + /* SRTP */ #if PJMEDIA_HAS_SRTP if (app_config.cfg.use_srtp != PJSUA_DEFAULT_USE_SRTP) { diff --git a/pjsip-apps/src/pjsua/pjsua_app_legacy.c b/pjsip-apps/src/pjsua/pjsua_app_legacy.c index afa64439f1..ee0918866d 100644 --- a/pjsip-apps/src/pjsua/pjsua_app_legacy.c +++ b/pjsip-apps/src/pjsua/pjsua_app_legacy.c @@ -245,8 +245,8 @@ static void keystroke_help() puts("| X Xfer with Replaces | | |"); puts("| # Send RFC 2833 DTMF | cl List ports | d Dump status |"); puts("| * Send DTMF with INFO | cc Connect port | dd Dump detailed |"); - puts("| dq Dump curr. call quality | cd Disconnect port | dc Dump config |"); - puts("| | V Adjust audio Volume | f Save config |"); + puts("| rt Send real-time text | cd Disconnect port | dc Dump config |"); + puts("| dq Dump curr. call quality | V Adjust audio Volume | f Save config |"); puts("| S Send arbitrary REQUEST | Cp Codec priorities | |"); puts("+-----------------------------------------------------------------------------+"); #if PJSUA_HAS_VIDEO @@ -1044,6 +1044,7 @@ static void ui_add_account(pjsua_transport_config *rtp_cfg) acc_cfg.cred_info[0].data = pj_str(passwd); acc_cfg.rtp_cfg = *rtp_cfg; + acc_cfg.txt_red_level = app_config.txt_red_level; app_config_init_video(&acc_cfg); status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL); @@ -1851,6 +1852,36 @@ static void ui_handle_ip_change() } } +static void ui_send_rtt() +{ + char buf[100]; + pjsua_call_send_text_param param; + pj_status_t status; + + if (current_call == PJSUA_INVALID_ID) { + PJ_LOG(3,(THIS_FILE, "No current call")); + return; + } + + if (!simple_input("Enter text to send", buf, sizeof(buf))) + return; + + pjsua_call_send_text_param_default(¶m); + param.text = pj_str(buf); + status = pjsua_call_send_text(current_call, ¶m); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to send text", status); + } + +#if 0 + // For testing buffering and redundancy + pj_str_t abc = pj_str("abc"); + pj_str_t def = pj_str("defgh"); + pjsua_call_send_text(current_call, -1, &abc); + pjsua_call_send_text(current_call, -1, &def); +#endif +} + /* * Main "user interface" loop. */ @@ -1897,6 +1928,7 @@ void legacy_main(void) pjsua_call_setting_default(&call_opt); call_opt.aud_cnt = app_config.aud_cnt; call_opt.vid_cnt = app_config.vid.vid_cnt; + call_opt.txt_cnt = app_config.txt_cnt; switch (menuin[0]) { @@ -2060,7 +2092,11 @@ void legacy_main(void) break; case 'r': - ui_register(menuin); + if (menuin[1] == 't') { + ui_send_rtt(); + } else { + ui_register(menuin); + } break; case 't': diff --git a/pjsip-apps/src/samples/pjsua2_demo.cpp b/pjsip-apps/src/samples/pjsua2_demo.cpp index 2aa30be7d5..05a27b07b7 100644 --- a/pjsip-apps/src/samples/pjsua2_demo.cpp +++ b/pjsip-apps/src/samples/pjsua2_demo.cpp @@ -95,6 +95,8 @@ class MyCall : public Call virtual void onCallTransferRequest(OnCallTransferRequestParam &prm); virtual void onCallReplaceRequest(OnCallReplaceRequestParam &prm); virtual void onCallMediaState(OnCallMediaStateParam &prm); + + virtual void onCallRxText(OnCallRxTextParam &prm); }; class MyAccount : public Account @@ -232,6 +234,18 @@ void MyCall::onCallReplaceRequest(OnCallReplaceRequestParam &prm) prm.newCall = new MyCall(*myAcc); } +void MyCall::onCallRxText(OnCallRxTextParam &prm) +{ + if (prm.text.empty()) { + std::cout << "Received empty T140 block with seq " << prm.seq; + std::cout << std::endl; + } else { + std::cout << "Incoming text with seq " << prm.seq << ": " << prm.text; + std::cout << std::endl; + } +} + + #if USE_TEST == 1 static void mainProg1(MyEndpoint &ep) { @@ -251,14 +265,14 @@ static void mainProg1(MyEndpoint &ep) // Add account AccountConfig acc_cfg; - acc_cfg.idUri = "sip:401@pjsip.org"; + acc_cfg.idUri = "sip:test@pjsip.org"; acc_cfg.regConfig.registrarUri = "sip:sip.pjsip.org"; #if PJSIP_HAS_DIGEST_AKA_AUTH AuthCredInfo aci("Digest", "*", "test", PJSIP_CRED_DATA_EXT_AKA | PJSIP_CRED_DATA_PLAIN_PASSWD, "passwd"); aci.akaK = "passwd"; #else - AuthCredInfo aci("digest", "*", "401", 0, "pw401"); + AuthCredInfo aci("digest", "*", "test", 0, "test"); #endif acc_cfg.sipConfig.authCreds.push_back(PJSUA2_MOVE(aci)); @@ -277,7 +291,13 @@ static void mainProg1(MyEndpoint &ep) CallOpParam prm(true); prm.opt.audioCount = 1; prm.opt.videoCount = 0; - call->makeCall("sip:402@sip.pjsip.org", prm); + prm.opt.textCount = 1; + call->makeCall("sip:test@sip.pjsip.org", prm); + + pj_thread_sleep(1000); + CallSendTextParam param; + param.text = "PJSIP"; + call->sendText(param); // Hangup all calls pj_thread_sleep(4000); diff --git a/pjsip/build/Makefile b/pjsip/build/Makefile index c3146b9b2b..66693c33a5 100644 --- a/pjsip/build/Makefile +++ b/pjsip/build/Makefile @@ -114,7 +114,7 @@ export PJSUA_LIB_SRCDIR = ../src/pjsua-lib export PJSUA_LIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ pjsua_acc.o pjsua_call.o pjsua_core.o \ pjsua_im.o pjsua_media.o pjsua_pres.o \ - pjsua_dump.o pjsua_aud.o pjsua_vid.o + pjsua_dump.o pjsua_aud.o pjsua_txt.o pjsua_vid.o export PJSUA_LIB_CFLAGS += $(_CFLAGS) $(PJ_VIDEO_CFLAGS) export PJSUA_LIB_CXXFLAGS += $(_CXXFLAGS) $(PJ_VIDEO_CFLAGS) export PJSUA_LIB_LDFLAGS += $(PJSIP_UA_LDLIB) \ diff --git a/pjsip/build/pjsua_lib.vcproj b/pjsip/build/pjsua_lib.vcproj index 0296141fb4..711adcf444 100644 --- a/pjsip/build/pjsua_lib.vcproj +++ b/pjsip/build/pjsua_lib.vcproj @@ -3804,6 +3804,10 @@ /> + + diff --git a/pjsip/build/pjsua_lib.vcxproj b/pjsip/build/pjsua_lib.vcxproj index 8af5143510..454e350cdb 100644 --- a/pjsip/build/pjsua_lib.vcxproj +++ b/pjsip/build/pjsua_lib.vcxproj @@ -682,6 +682,7 @@ + diff --git a/pjsip/build/pjsua_lib.vcxproj.filters b/pjsip/build/pjsua_lib.vcxproj.filters index 9a326cce31..6173e53cf6 100644 --- a/pjsip/build/pjsua_lib.vcxproj.filters +++ b/pjsip/build/pjsua_lib.vcxproj.filters @@ -35,6 +35,9 @@ Source Files + + Source Files + Source Files diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 3ee85bec84..0fdf4f8b4f 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -377,6 +377,15 @@ typedef struct pj_stun_resolve_result pj_stun_resolve_result; #endif +/** + * Default redundancy level for text streams. If this macro is set to zero, + * it means that there will be no redundancy. + */ +#ifndef PJSUA_TXT_DEFAULT_REDUNDANCY_LEVEL +# define PJSUA_TXT_DEFAULT_REDUNDANCY_LEVEL 2 +#endif + + /** * Specify whether timer heap events will be polled by a separate worker * thread. If this is set/enabled, a worker thread will be dedicated to @@ -588,6 +597,9 @@ typedef struct pjsua_stream_info /** Video stream info */ pjmedia_vid_stream_info vid; + + /** Text stream info */ + pjmedia_txt_stream_info txt; } info; } pjsua_stream_info; @@ -1032,6 +1044,29 @@ typedef struct pjsua_dtmf_event { } pjsua_dtmf_event; +/** + * This will contain the information of the callback \a on_call_rx_text(). + */ +typedef struct pjsua_txt_stream_data { + /** + * The sequence of the incoming text block data. + */ + int seq; + + /** + * The timestamp of the text block data. + */ + unsigned ts; + + /** + * The content of the text block. + * Note that the text can be empty. + */ + pj_str_t text; + +} pjsua_txt_stream_data; + + /** * Call settings. */ @@ -1040,7 +1075,8 @@ typedef struct pjsua_call_setting /** * Bitmask of #pjsua_call_flag constants. * - * Default: PJSUA_CALL_INCLUDE_DISABLED_MEDIA + * Default: 0 + * (PJSUA_CALL_INCLUDE_DISABLED_MEDIA is the legacy default value). */ unsigned flag; @@ -1069,6 +1105,14 @@ typedef struct pjsua_call_setting */ unsigned vid_cnt; + /** + * Number of simultaneous active text streams for this call. Setting + * this to zero will disable text in this call. + * + * Default: 1 + */ + unsigned txt_cnt; + /** * Media direction. This setting will only be used if the flag * PJSUA_CALL_SET_MEDIA_DIR is set, and it will persist for subsequent @@ -1346,6 +1390,19 @@ typedef struct pjsua_callback void (*on_dtmf_event)(pjsua_call_id call_id, const pjsua_dtmf_event *event); + /** + * Notify application upon incoming text data from the text stream. + * Note that the received text can be empty. + * + * IMPORTANT: Application shall refrain from invoking call APIs from + * within the callback, such as call re-invite or hangup. + * + * @param call_id The call index. + * @param data The text data. + */ + void (*on_call_rx_text)(pjsua_call_id call_id, + const pjsua_txt_stream_data *data); + /** * Notify application on call being transferred (i.e. REFER is received). * Application can decide to accept/reject transfer request @@ -4312,6 +4369,31 @@ typedef struct pjsua_acc_config */ pj_str_t ka_data; + /** + * Specifies text stream redundancy level, as specified in RFC 4103 + * and 2198. When redundancy is enabled, each packet transmission + * will contain the current text data as well as a number of the + * previously transmitted text data to provide levels of redundancy. + * This mechanism offers protection against loss of data at the cost + * of additional bandwidth required. + * + * Value is integer indicating redundancy levels, i.e. the number + * of previous text data to be included with the current packet. + * (0 means disabled/no redundancy). + * A value of 1 provides an adequate protection against an average + * packet loss of up to 50%, while 2 can potentially protect + * against 66.7%. + * The maximum value is determined by PJMEDIA_TXT_STREAM_MAX_RED_LEVELS. + * + * Note that the redundancy level actually used is subject to remote + * capability and we will opt to use the lower redundancy value based + * on the result of SDP negotiation. + * + * Default: PJSUA_TXT_DEFAULT_REDUNDANCY_LEVEL (2), as per the + * recommendation of RFC 4103. + */ + int txt_red_level; + /** * Specify whether incoming video should be shown to screen by default. * This applies to incoming call (INVITE), incoming re-INVITE, and @@ -5486,6 +5568,9 @@ typedef struct pjsua_call_info /** Number of video streams offered by remote */ unsigned rem_vid_cnt; + /** Number of text streams offered by remote */ + unsigned rem_txt_cnt; + /** Internal */ struct { char local_info[PJSIP_MAX_URL_SIZE]; @@ -5730,6 +5815,28 @@ typedef struct pjsua_call_send_dtmf_param } pjsua_call_send_dtmf_param; +/** + * Parameters for sending text. Application should use + * #pjsua_call_send_text_param_default() to initialize this structure + * with its default values. + */ +typedef struct pjsua_call_send_text_param +{ + /** + * Text media stream index. + * + * Default: -1 (use the first text media). + */ + int med_idx; + + /** + * The text to be sent. + */ + pj_str_t text; + +} pjsua_call_send_text_param; + + /** * Initialize call settings. * @@ -5752,9 +5859,16 @@ pjsua_call_vid_strm_op_param_default(pjsua_call_vid_strm_op_param *param); * * @param param The send DTMF param to be initialized. */ -PJ_DECL(void) +PJ_DECL(void) pjsua_call_send_dtmf_param_default(pjsua_call_send_dtmf_param *param); +/** + * Initialize send DTMF param with default values. + * + * @param param The send DTMF param to be initialized. + */ +PJ_DECL(void) +pjsua_call_send_text_param_default(pjsua_call_send_text_param *param); /** * Get maximum number of calls configured in pjsua. @@ -6319,6 +6433,18 @@ PJ_DECL(pj_status_t) pjsua_call_dial_dtmf2(pjsua_call_id call_id, PJ_DECL(pj_status_t) pjsua_call_send_dtmf(pjsua_call_id call_id, const pjsua_call_send_dtmf_param *param); +/** + * Send real-time text to remote via RTP stream. This only works if the call + * has text media. + * + * @param call_id Call identification. + * @param param The send text parameter. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsua_call_send_text(pjsua_call_id call_id, + const pjsua_call_send_text_param *param); + /** * Send instant messaging inside INVITE session. * diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index b14f658730..d2bb7ed816 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -68,6 +68,11 @@ struct pjsua_call_media pjmedia_vid_dev_index rdr_dev; /**< The video-in render device */ } v; + /** Text stream */ + struct { + pjmedia_txt_stream *stream; /**< The text stream. */ + } t; + } strm; pj_uint32_t ssrc; /**< RTP SSRC */ @@ -213,6 +218,8 @@ struct pjsua_call offer. */ unsigned rem_vid_cnt; /**< No of active video in last remote offer. */ + unsigned rem_txt_cnt; /**< No of active text in last remote + offer. */ pj_bool_t rx_reinv_async;/**< on_call_rx_reinvite() async. */ pj_timer_entry reinv_timer; /**< Reinvite retry timer. */ @@ -969,6 +976,16 @@ void pjsua_vid_win_reset(pjsua_vid_win_id wid); # define pjsua_vid_win_reset(wid) #endif +/* + * Text + */ +void pjsua_txt_stop_stream(pjsua_call_media *call_med); +pj_status_t pjsua_txt_channel_update(pjsua_call_media *call_med, + pj_pool_t *tmp_pool, + pjmedia_txt_stream_info *si, + const pjmedia_sdp_session *local_sdp, + const pjmedia_sdp_session *remote_sdp); + /* * Schedule check for the need of re-INVITE/UPDATE after media update */ diff --git a/pjsip/include/pjsua2/account.hpp b/pjsip/include/pjsua2/account.hpp index bc4e2f1e99..bbf7d9aaf1 100644 --- a/pjsip/include/pjsua2/account.hpp +++ b/pjsip/include/pjsua2/account.hpp @@ -1055,7 +1055,7 @@ struct RtcpFbConfig : public PersistentObject }; /** - * Account media config (applicable for both audio and video). This will be + * Account media config (applicable for audio, video, and text). This will be * specified in AccountConfig. */ struct AccountMediaConfig : public PersistentObject @@ -1313,6 +1313,59 @@ struct AccountVideoConfig : public PersistentObject virtual void writeObject(ContainerNode &node) const PJSUA2_THROW(Error); }; +/** + * Account text config. This will be specified in AccountConfig. + */ +struct AccountTextConfig : public PersistentObject +{ + /** + * Specifies text stream redundancy level, as specified in RFC 4103 + * and 2198. When redundancy is enabled, each packet transmission + * will contain the current text data as well as a number of the + * previously transmitted text data to provide levels of redundancy. + * This mechanism offers protection against loss of data at the cost + * of additional bandwidth required. + * + * Value is integer indicating redundancy levels, i.e. the number + * of previous text data to be included with the current packet. + * (0 means disabled/no redundancy). + * A value of 1 provides an adequate protection against an average + * packet loss of up to 50%, while 2 can potentially protect + * against 66.7%. + * The maximum value is determined by PJMEDIA_TXT_STREAM_MAX_RED_LEVELS. + * + * Note that the redundancy level actually used is subject to remote + * capability and we will opt to use the lower redundancy value based + * on the result of SDP negotiation. + * + * Default: PJSUA_TXT_DEFAULT_REDUNDANCY_LEVEL (2), as per the + * recommendation of RFC 4103. + */ + int redundancyLevel; + +public: + /** + * Default constructor + */ + AccountTextConfig() + : redundancyLevel(PJSUA_TXT_DEFAULT_REDUNDANCY_LEVEL) + {} + + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) PJSUA2_THROW(Error); + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const PJSUA2_THROW(Error); +}; + /** * Account config specific to IP address change. */ @@ -1432,7 +1485,7 @@ struct AccountConfig : public PersistentObject AccountNatConfig natConfig; /** - * Media settings (applicable for both audio and video). + * Media settings (applicable for audio, video, and text). */ AccountMediaConfig mediaConfig; @@ -1441,6 +1494,11 @@ struct AccountConfig : public PersistentObject */ AccountVideoConfig videoConfig; + /** + * Text settings. + */ + AccountTextConfig textConfig; + /** * IP Change settings. */ diff --git a/pjsip/include/pjsua2/call.hpp b/pjsip/include/pjsua2/call.hpp index 743da677ac..d9ec0b8ef8 100644 --- a/pjsip/include/pjsua2/call.hpp +++ b/pjsip/include/pjsua2/call.hpp @@ -281,7 +281,8 @@ struct CallSetting /** * Bitmask of pjsua_call_flag constants. * - * Default: PJSUA_CALL_INCLUDE_DISABLED_MEDIA + * Default: 0 + * (PJSUA_CALL_INCLUDE_DISABLED_MEDIA is the legacy default value). */ unsigned flag; @@ -310,6 +311,14 @@ struct CallSetting */ unsigned videoCount; + /** + * Number of simultaneous active text streams for this call. Setting + * this to zero will disable text in this call. + * + * Default: 1 + */ + unsigned textCount; + /** * Media direction. This setting will only be used if the flag * PJSUA_CALL_SET_MEDIA_DIR is set, and it will persist for subsequent @@ -553,6 +562,11 @@ struct CallInfo */ unsigned remVideoCount; + /** + * Number of text streams offered by remote + */ + unsigned remTextCount; + public: /** * Default constructor @@ -564,7 +578,8 @@ struct CallInfo lastStatusCode(PJSIP_SC_NULL), remOfferer(false), remAudioCount(0), - remVideoCount(0) + remVideoCount(0), + remTextCount(0) {} /** @@ -919,6 +934,35 @@ struct OnDtmfEventParam unsigned flags; }; +/** + * This structure contains parameters for Call::onCallRxText() + * callback. + */ +struct OnCallRxTextParam +{ + /** + * The sequence of the incoming text block data. + */ + int seq; + + /** + * The timestamp of the text block data. + */ + unsigned ts; + + /** + * The content of the text block. + * Note that the text can be empty. + */ + string text; + +public: + /** + * Convert from pjsip + */ + void fromPj(const pjsua_txt_stream_data &prm); +}; + /** * This structure contains parameters for Call::onCallTransferRequest() * callback. @@ -1383,6 +1427,32 @@ struct CallSendDtmfParam void fromPj(const pjsua_call_send_dtmf_param ¶m); }; +/** + * This structure contains parameters for Call::sendText() + */ +struct CallSendTextParam +{ + /** + * Specify the text media stream index. This can be set to -1 to denote + * the first text stream in the call. + * + * Default: -1 (first text stream) + */ + int medIdx; + + /** + * The text data to be sent. + */ + string text; + +public: + /** + * Default constructor initializes with default value. + */ + CallSendTextParam(); +}; + + /** * Call. */ @@ -1713,6 +1783,14 @@ class Call */ void sendDtmf(const CallSendDtmfParam ¶m) PJSUA2_THROW(Error); + /** + * Send real-time text to remote via RTP stream. This only works if + * the call has text media. + * + * @param param The send text parameter. + */ + void sendText(const CallSendTextParam ¶m) PJSUA2_THROW(Error); + /** * Send instant messaging inside INVITE session. * @@ -1969,6 +2047,15 @@ class Call virtual void onDtmfEvent(OnDtmfEventParam &prm) { PJ_UNUSED_ARG(prm); } + /** + * Notify application upon incoming text data from the text stream. + * Note that the received text can be empty. + * + * @param prm Callback parameter. + */ + virtual void onCallRxText(OnCallRxTextParam &prm) + { PJ_UNUSED_ARG(prm); } + /** * Notify application on call being transferred (i.e. REFER is received). * Application can decide to accept/reject transfer request by setting diff --git a/pjsip/include/pjsua2/endpoint.hpp b/pjsip/include/pjsua2/endpoint.hpp index 2139579db3..d135293509 100644 --- a/pjsip/include/pjsua2/endpoint.hpp +++ b/pjsip/include/pjsua2/endpoint.hpp @@ -2111,6 +2111,8 @@ class Endpoint const pjsua_dtmf_info *info); static void on_dtmf_event(pjsua_call_id call_id, const pjsua_dtmf_event *event); + static void on_call_rx_text(pjsua_call_id call_id, + const pjsua_txt_stream_data *data); static void on_call_transfer_request(pjsua_call_id call_id, const pj_str_t *dst, pjsip_status_code *code); diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index 689e0cf6fd..ff57a1733b 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -129,116 +129,6 @@ pjsua_call_aud_stream_modify_codec_param(pjsua_call_id call_id, } -/* - * Get media stream info for the specified media index. - */ -PJ_DEF(pj_status_t) pjsua_call_get_stream_info( pjsua_call_id call_id, - unsigned med_idx, - pjsua_stream_info *psi) -{ - pjsua_call *call; - pjsua_call_media *call_med; - pj_status_t status = PJ_EINVAL; - - PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, - PJ_EINVAL); - PJ_ASSERT_RETURN(psi, PJ_EINVAL); - - PJSUA_LOCK(); - - call = &pjsua_var.calls[call_id]; - - if (med_idx >= call->med_cnt) - goto on_return; - - call_med = &call->media[med_idx]; - - if ((call_med->type == PJMEDIA_TYPE_AUDIO && !call_med->strm.a.stream) || - (call_med->type == PJMEDIA_TYPE_VIDEO && !call_med->strm.v.stream)) - { - goto on_return; - } - - psi->type = call_med->type; - switch (call_med->type) { - case PJMEDIA_TYPE_AUDIO: - status = pjmedia_stream_get_info(call_med->strm.a.stream, - &psi->info.aud); - break; -#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) - case PJMEDIA_TYPE_VIDEO: - status = pjmedia_vid_stream_get_info(call_med->strm.v.stream, - &psi->info.vid); - break; -#endif - default: - status = PJMEDIA_EINVALIMEDIATYPE; - break; - } - -on_return: - PJSUA_UNLOCK(); - return status; -} - - -/* - * Get media stream statistic for the specified media index. - */ -PJ_DEF(pj_status_t) pjsua_call_get_stream_stat( pjsua_call_id call_id, - unsigned med_idx, - pjsua_stream_stat *stat) -{ - pjsua_call *call; - pjsua_call_media *call_med; - pj_status_t status = PJ_EINVAL; - - PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, - PJ_EINVAL); - PJ_ASSERT_RETURN(stat, PJ_EINVAL); - - PJSUA_LOCK(); - - call = &pjsua_var.calls[call_id]; - - if (med_idx >= call->med_cnt) - goto on_return; - - call_med = &call->media[med_idx]; - - if ((call_med->type == PJMEDIA_TYPE_AUDIO && !call_med->strm.a.stream) || - (call_med->type == PJMEDIA_TYPE_VIDEO && !call_med->strm.v.stream)) - { - goto on_return; - } - - switch (call_med->type) { - case PJMEDIA_TYPE_AUDIO: - status = pjmedia_stream_get_stat(call_med->strm.a.stream, - &stat->rtcp); - if (status == PJ_SUCCESS) - status = pjmedia_stream_get_stat_jbuf(call_med->strm.a.stream, - &stat->jbuf); - break; -#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) - case PJMEDIA_TYPE_VIDEO: - status = pjmedia_vid_stream_get_stat(call_med->strm.v.stream, - &stat->rtcp); - if (status == PJ_SUCCESS) - status = pjmedia_vid_stream_get_stat_jbuf(call_med->strm.v.stream, - &stat->jbuf); - break; -#endif - default: - status = PJMEDIA_EINVALIMEDIATYPE; - break; - } - -on_return: - PJSUA_UNLOCK(); - return status; -} - /* * Send DTMF digits to remote using RFC 2833 payload formats. */ diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 2b51aa8b87..35ffefc350 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -648,8 +648,9 @@ PJ_DEF(void) pjsua_call_setting_default(pjsua_call_setting *opt) pj_assert(opt); pj_bzero(opt, sizeof(*opt)); - opt->flag = PJSUA_CALL_INCLUDE_DISABLED_MEDIA; + opt->flag = 0; //PJSUA_CALL_INCLUDE_DISABLED_MEDIA; opt->aud_cnt = 1; + opt->txt_cnt = 1; #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) opt->vid_cnt = 1; @@ -672,6 +673,17 @@ PJ_DEF(void) pjsua_call_send_dtmf_param_default( param->duration = PJSUA_CALL_SEND_DTMF_DURATION_DEFAULT; } +/* + * Initialize pjsua_call_send_text_param default values. + */ +PJ_DEF(void) pjsua_call_send_text_param_default( + pjsua_call_send_text_param *param) +{ + pj_bzero(param, sizeof(*param)); + param->med_idx = -1; +} + + static pj_status_t apply_call_setting(pjsua_call *call, const pjsua_call_setting *opt, const pjmedia_sdp_session *rem_sdp) @@ -1210,6 +1222,7 @@ pj_status_t create_temp_sdp(pj_pool_t *pool, { const pj_str_t STR_AUDIO = { "audio", 5 }; const pj_str_t STR_VIDEO = { "video", 5 }; + const pj_str_t STR_TEXT = { "text", 4 }; const pj_str_t STR_IP6 = { "IP6", 3}; pjmedia_sdp_session *sdp; @@ -1274,6 +1287,14 @@ pj_status_t create_temp_sdp(pj_pool_t *pool, #else m = pjmedia_sdp_media_clone_deactivate(pool, rem_sdp->media[i]); #endif + } else if (pj_stricmp(&rem_sdp->media[i]->desc.media, &STR_TEXT)==0) { + m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media); + status = pjmedia_endpt_create_text_sdp(pjsua_var.med_endpt, + pool, &sock_info, 0, &m); + + if (status != PJ_SUCCESS) + return status; + } else { m = pjmedia_sdp_media_clone_deactivate(pool, rem_sdp->media[i]); } @@ -2499,6 +2520,7 @@ PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id, if (call->rem_offerer) { info->rem_aud_cnt = call->rem_aud_cnt; info->rem_vid_cnt = call->rem_vid_cnt; + info->rem_txt_cnt = call->rem_txt_cnt; } /* Build array of active media info */ @@ -2531,6 +2553,7 @@ PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id, cap_dev = call_med->strm.v.cap_dev; } info->media[info->media_cnt].stream.vid.cap_dev = cap_dev; + } else if (call_med->type == PJMEDIA_TYPE_TEXT) { } else { continue; } @@ -2567,6 +2590,7 @@ PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id, cap_dev = call_med->strm.v.cap_dev; } info->prov_media[info->prov_media_cnt].stream.vid.cap_dev=cap_dev; + } else if (call_med->type == PJMEDIA_TYPE_TEXT) { } else { continue; } @@ -4114,6 +4138,7 @@ static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m, const pj_str_t *fmt) { const pj_str_t STR_TEL = {"telephone-event", 15}; + const pj_str_t STR_RED = {"red", 3}; unsigned pt; pt = pj_strtoul(fmt); @@ -4130,9 +4155,12 @@ static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m, /* Get the format name */ a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt); if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) { - /* Check for telephone-event */ - if (pj_stricmp(&rtpmap.enc_name, &STR_TEL)==0) + /* Check for telephone-event and redundancy*/ + if (pj_stricmp(&rtpmap.enc_name, &STR_TEL)==0 || + pj_stricmp(&rtpmap.enc_name, &STR_RED)==0) + { return PJ_TRUE; + } } else { /* Invalid SDP, should not reach here */ pj_assert(!"SDP should have been validated!"); @@ -6795,3 +6823,124 @@ static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv, return op; } +#if defined(PJSUA_MEDIA_HAS_PJMEDIA) && PJSUA_MEDIA_HAS_PJMEDIA != 0 + +/* + * Get media stream info for the specified media index. + */ +PJ_DEF(pj_status_t) pjsua_call_get_stream_info( pjsua_call_id call_id, + unsigned med_idx, + pjsua_stream_info *psi) +{ + pjsua_call *call; + pjsua_call_media *call_med; + pj_status_t status = PJ_EINVAL; + + PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, + PJ_EINVAL); + PJ_ASSERT_RETURN(psi, PJ_EINVAL); + + PJSUA_LOCK(); + + call = &pjsua_var.calls[call_id]; + + if (med_idx >= call->med_cnt) + goto on_return; + + call_med = &call->media[med_idx]; + + if ((call_med->type == PJMEDIA_TYPE_AUDIO && !call_med->strm.a.stream) || + (call_med->type == PJMEDIA_TYPE_VIDEO && !call_med->strm.v.stream) || + (call_med->type == PJMEDIA_TYPE_TEXT && !call_med->strm.t.stream)) + { + goto on_return; + } + + psi->type = call_med->type; + switch (call_med->type) { + case PJMEDIA_TYPE_AUDIO: + status = pjmedia_stream_get_info(call_med->strm.a.stream, + &psi->info.aud); + break; +#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) + case PJMEDIA_TYPE_VIDEO: + status = pjmedia_vid_stream_get_info(call_med->strm.v.stream, + &psi->info.vid); + break; +#endif + case PJMEDIA_TYPE_TEXT: + status = pjmedia_txt_stream_get_info(call_med->strm.t.stream, + &psi->info.txt); + break; + default: + status = PJMEDIA_EINVALIMEDIATYPE; + break; + } + +on_return: + PJSUA_UNLOCK(); + return status; +} + + +/* + * Get media stream statistic for the specified media index. + */ +PJ_DEF(pj_status_t) pjsua_call_get_stream_stat( pjsua_call_id call_id, + unsigned med_idx, + pjsua_stream_stat *stat) +{ + pjsua_call *call; + pjsua_call_media *call_med; + pjmedia_stream_common *c_strm = NULL; + pj_status_t status = PJ_EINVAL; + + PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, + PJ_EINVAL); + PJ_ASSERT_RETURN(stat, PJ_EINVAL); + + PJSUA_LOCK(); + + call = &pjsua_var.calls[call_id]; + + if (med_idx >= call->med_cnt) + goto on_return; + + call_med = &call->media[med_idx]; + + if ((call_med->type == PJMEDIA_TYPE_AUDIO && !call_med->strm.a.stream) || + (call_med->type == PJMEDIA_TYPE_VIDEO && !call_med->strm.v.stream) || + (call_med->type == PJMEDIA_TYPE_TEXT && !call_med->strm.t.stream)) + { + goto on_return; + } + + switch (call_med->type) { + case PJMEDIA_TYPE_AUDIO: + c_strm = (pjmedia_stream_common *)call_med->strm.a.stream; + break; +#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) + case PJMEDIA_TYPE_VIDEO: + c_strm = (pjmedia_stream_common *)call_med->strm.v.stream; + break; +#endif + case PJMEDIA_TYPE_TEXT: + c_strm = (pjmedia_stream_common *)call_med->strm.t.stream; + break; + default: + status = PJMEDIA_EINVALIMEDIATYPE; + break; + } + + if (c_strm) { + status = pjmedia_stream_common_get_stat(c_strm, &stat->rtcp); + if (status == PJ_SUCCESS) + status = pjmedia_stream_common_get_stat_jbuf(c_strm, &stat->jbuf); + } + +on_return: + PJSUA_UNLOCK(); + return status; +} + +#endif diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 176e8d6309..5c8bfd93e7 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -340,6 +340,7 @@ PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg) cfg->lock_codec = 1; cfg->ka_interval = 15; cfg->ka_data = pj_str("\r\n"); + cfg->txt_red_level = PJSUA_TXT_DEFAULT_REDUNDANCY_LEVEL; cfg->vid_cap_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV; cfg->vid_rend_dev = PJMEDIA_VID_DEFAULT_RENDER_DEV; #if PJMEDIA_HAS_VIDEO diff --git a/pjsip/src/pjsua-lib/pjsua_dump.c b/pjsip/src/pjsua-lib/pjsua_dump.c index cad49bd345..22aee65bb2 100644 --- a/pjsip/src/pjsua-lib/pjsua_dump.c +++ b/pjsip/src/pjsua-lib/pjsua_dump.c @@ -256,6 +256,9 @@ static void dump_media_session(const char *indent, case PJMEDIA_TYPE_VIDEO: media_type_str = "video"; break; + case PJMEDIA_TYPE_TEXT: + media_type_str = "text"; + break; case PJMEDIA_TYPE_APPLICATION: media_type_str = "application"; break; @@ -266,7 +269,8 @@ static void dump_media_session(const char *indent, /* Check if the stream is deactivated */ if (call_med->tp == NULL || - (!call_med->strm.a.stream && !call_med->strm.v.stream)) + (!call_med->strm.a.stream && !call_med->strm.v.stream && + !call_med->strm.t.stream)) { len = pj_ansi_snprintf(p, end-p, "%s #%d %s deactivated\n", @@ -363,6 +367,23 @@ static void dump_media_session(const char *indent, } #endif /* PJMEDIA_HAS_VIDEO */ + } else if (call_med->type == PJMEDIA_TYPE_TEXT) { + pjmedia_txt_stream *stream = call_med->strm.t.stream; + pjmedia_txt_stream_info info; + + pjmedia_stream_common_get_stat((pjmedia_stream_common *)stream, + &stat); + has_stat = PJ_TRUE; + + pjmedia_txt_stream_get_info(stream, &info); + pj_ansi_snprintf(codec_info, sizeof(codec_info), " %.*s", + (int)info.fmt.encoding_name.slen, + info.fmt.encoding_name.ptr); + pj_ansi_snprintf(rx_info, sizeof(rx_info), "pt=%d, red=%d(lvl=%d)", + info.rx_pt, info.rx_red_pt, info.rx_red_level); + pj_ansi_snprintf(tx_info, sizeof(tx_info), "pt=%d, red=%d(lvl=%d),", + info.tx_pt, info.tx_red_pt, info.tx_red_level); + } else { has_stat = PJ_FALSE; } diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index 43e3960ecf..d40665ab40 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -2401,6 +2401,7 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, { const pj_str_t STR_AUDIO = { "audio", 5 }; const pj_str_t STR_VIDEO = { "video", 5 }; + const pj_str_t STR_TEXT = { "text", 4 }; pjsua_call *call = &pjsua_var.calls[call_id]; pjsua_acc *acc = &pjsua_var.acc[call->acc_id]; pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA]; @@ -2409,6 +2410,9 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, pj_uint8_t mvididx[PJSUA_MAX_CALL_MEDIA]; unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx); unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx); + pj_uint8_t mtxtidx[PJSUA_MAX_CALL_MEDIA]; + unsigned mtxtcnt = PJ_ARRAY_SIZE(mtxtidx); + unsigned mtottxtcnt = PJ_ARRAY_SIZE(mtxtidx); unsigned mi; pj_bool_t pending_med_tp = PJ_FALSE; pj_bool_t reinit = PJ_FALSE; @@ -2494,8 +2498,11 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, PJ_UNUSED_ARG(STR_VIDEO); #endif - if (maudcnt + mvidcnt == 0) { - /* Expecting audio or video in the offer */ + sort_media(rem_sdp, &STR_TEXT, acc->cfg.use_srtp, + mtxtidx, &mtxtcnt, &mtottxtcnt); + + if (maudcnt + mvidcnt + mtxtcnt == 0) { + /* Expecting media in the offer */ if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE; status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE); @@ -2513,6 +2520,7 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, call->rem_offerer = PJ_TRUE; call->rem_aud_cnt = maudcnt; call->rem_vid_cnt = mvidcnt; + call->rem_txt_cnt = mtxtcnt; } else { @@ -2542,6 +2550,9 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, sort_media2(call->media_prov, sort_check_tp, call->med_prov_cnt, PJMEDIA_TYPE_VIDEO, mvididx, &mvidcnt, &mtotvidcnt); + sort_media2(call->media_prov, sort_check_tp, call->med_prov_cnt, + PJMEDIA_TYPE_TEXT, mtxtidx, &mtxtcnt, &mtottxtcnt); + /* Call setting may add or remove media. Adding media is done by * enabling any disabled/port-zeroed media first, then adding new * media whenever needed. Removing media is done by disabling @@ -2570,6 +2581,17 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, } mvidcnt = call->opt.vid_cnt; + /* Check if we need to add new text */ + if (mtxtcnt < call->opt.txt_cnt && + mtottxtcnt < call->opt.txt_cnt) + { + for (mi = 0; mi < call->opt.txt_cnt - mtottxtcnt; ++mi) + mtxtidx[mtxtcnt++] = (pj_uint8_t)call->med_prov_cnt++; + + mtottxtcnt = call->opt.txt_cnt; + } + mtxtcnt = call->opt.txt_cnt; + /* In case of media reinit, 'med_prov_cnt' may be decreased * because the new call->opt says so. As media count should * never decrease, we should verify 'med_prov_cnt' to be @@ -2591,7 +2613,11 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, for (mi=0; mimed_prov_cnt = maudcnt + mvidcnt; + mtxtcnt = mtottxtcnt = call->opt.txt_cnt; + for (mi=0; mimed_prov_cnt = maudcnt + mvidcnt + mtxtcnt; /* Need to publish supported media? */ if (call->opt.flag & PJSUA_CALL_INCLUDE_DISABLED_MEDIA) { @@ -2605,6 +2631,10 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, mvididx[0] = (pj_uint8_t)call->med_prov_cnt++; } #endif + if (mtottxtcnt == 0) { + mtottxtcnt = 1; + mtxtidx[0] = (pj_uint8_t)call->med_prov_cnt++; + } } } @@ -2652,6 +2682,13 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, { enabled = PJ_TRUE; } + } else if (pj_memchr(mtxtidx, mi, mtottxtcnt * sizeof(mtxtidx[0]))) { + media_type = PJMEDIA_TYPE_TEXT; + if (call->opt.txt_cnt && + pj_memchr(mtxtidx, mi, mtxtcnt * sizeof(mtxtidx[0]))) + { + enabled = PJ_TRUE; + } } if (call->opt.flag & PJSUA_CALL_SET_MEDIA_DIR) { @@ -2915,6 +2952,10 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, m->desc.media = pj_str("video"); m->desc.fmt[0] = pj_str("31"); break; + case PJMEDIA_TYPE_TEXT: + m->desc.media = pj_str("text"); + m->desc.fmt[0] = pj_str("0"); + break; default: /* This must be us generating re-offer, and some unknown * media may exist, so just clone from active local SDP @@ -2997,6 +3038,12 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, ¶m, &m); break; #endif + case PJMEDIA_TYPE_TEXT: + param.red_level = acc->cfg.txt_red_level; + status = pjmedia_endpt_create_text_sdp(pjsua_var.med_endpt, pool, + &tpinfo.sock_info, + ¶m, &m); + break; default: pj_assert(!"Invalid call_med media type"); status = PJ_EBUG; @@ -3296,6 +3343,9 @@ static void stop_media_stream(pjsua_call *call, unsigned med_idx) pjsua_vid_stop_stream(call_med); } #endif + else if (call_med->type == PJMEDIA_TYPE_TEXT) { + pjsua_txt_stop_stream(call_med); + } PJ_LOG(4,(THIS_FILE, "Media stream call%02d:%d is destroyed", call->index, med_idx)); @@ -3334,6 +3384,9 @@ static void stop_media_stream(pjsua_call *call, unsigned med_idx) prov_med->strm.v.stream = call_med->strm.v.stream; } #endif + else if (call_med->type == PJMEDIA_TYPE_TEXT) { + prov_med->strm.t.stream = call_med->strm.t.stream; + } } pj_log_pop_indent(); @@ -3561,6 +3614,7 @@ static void check_srtp_roc(pjsua_call *call, #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) pjmedia_vid_stream_info vid_si; #endif + pjmedia_txt_stream_info txt_si; pj_bool_t local_change = PJ_FALSE, rem_change = PJ_FALSE; pjmedia_srtp_setting setting; @@ -3628,6 +3682,14 @@ static void check_srtp_roc(pjsua_call *call, } } #endif + else if (call_med->type == PJMEDIA_TYPE_TEXT) { + /* Get current active text stream info */ + if (call_med->strm.t.stream) { + pjmedia_txt_stream_get_info(call_med->strm.t.stream, &txt_si); + prev_local_addr = &txt_si.local_addr; + prev_rem_addr = &txt_si.rem_addr; + } + } } #if 0 @@ -3652,7 +3714,10 @@ static void check_srtp_roc(pjsua_call *call, new_rem_addr = &new_si_->info.vid.rem_addr; } #endif - else { + else if (call_med->type == PJMEDIA_TYPE_TEXT) { + new_local_addr = &new_si_->info.txt.local_addr; + new_rem_addr = &new_si_->info.txt.rem_addr; + } else { /* Just return for other media type */ return; } @@ -3968,6 +4033,7 @@ static pj_status_t apply_med_update(pjsua_call_media *call_med, #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) pjmedia_vid_stream_info vsi = {0}; #endif + pjmedia_txt_stream_info tsi; pjmedia_stream_info_common *si = NULL; pjsua_stream_info stream_info = {0}; pj_str_t *enc_name = NULL; @@ -3989,6 +4055,13 @@ static pj_status_t apply_med_update(pjsua_call_media *call_med, stream_info.info.vid = vsi; enc_name = &vsi.codec_info.encoding_name; #endif + } else if (call_med->type == PJMEDIA_TYPE_TEXT) { + si = (pjmedia_stream_info_common *)&tsi; + status = pjmedia_txt_stream_info_from_sdp( + &tsi, tmp_pool, pjsua_var.med_endpt, + local_sdp, remote_sdp, mi); + stream_info.info.txt = tsi; + enc_name = &tsi.fmt.encoding_name; } else { status = PJMEDIA_EUNSUPMEDIATYPE; @@ -4210,15 +4283,19 @@ static pj_status_t apply_med_update(pjsua_call_media *call_med, /* Update audio channel */ if (media_changed) { if (call_med->type == PJMEDIA_TYPE_AUDIO) { - status = pjsua_aud_channel_update(call_med, - call->inv->pool, &asi, - local_sdp, remote_sdp); + status = pjsua_aud_channel_update(call_med, + call->inv->pool, &asi, + local_sdp, remote_sdp); } else if (call_med->type == PJMEDIA_TYPE_VIDEO) { #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) - status = pjsua_vid_channel_update(call_med, - call->inv->pool, &vsi, - local_sdp, remote_sdp); + status = pjsua_vid_channel_update(call_med, + call->inv->pool, &vsi, + local_sdp, remote_sdp); #endif + } else if (call_med->type == PJMEDIA_TYPE_TEXT) { + status = pjsua_txt_channel_update(call_med, + call->inv->pool, &tsi, + local_sdp, remote_sdp); } if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, @@ -4289,12 +4366,16 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, const pj_str_t STR_AUDIO = { "audio", 5 }; const pj_str_t STR_VIDEO = { "video", 5 }; + const pj_str_t STR_TEXT = { "text", 4 }; pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA]; unsigned maudcnt = PJ_ARRAY_SIZE(maudidx); unsigned mtotaudcnt = PJ_ARRAY_SIZE(maudidx); pj_uint8_t mvididx[PJSUA_MAX_CALL_MEDIA]; unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx); unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx); + pj_uint8_t mtxtidx[PJSUA_MAX_CALL_MEDIA]; + unsigned mtxtcnt = PJ_ARRAY_SIZE(mvididx); + unsigned mtottxtcnt = PJ_ARRAY_SIZE(mvididx); pj_bool_t need_renego_sdp = PJ_FALSE; if (pjsua_get_state() != PJSUA_STATE_RUNNING) @@ -4324,6 +4405,8 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, PJ_UNUSED_ARG(STR_VIDEO); mvidcnt = mtotvidcnt = 0; #endif + sort_media(local_sdp, &STR_TEXT, acc->cfg.use_srtp, + mtxtidx, &mtxtcnt, &mtottxtcnt); /* We need to re-nego SDP or modify our answer when: * - media count exceeds the configured limit, @@ -4331,7 +4414,8 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, * codecs) */ if (!pjmedia_sdp_neg_was_answer_remote(call->inv->neg) && - ((maudcnt > call->opt.aud_cnt || mvidcnt > call->opt.vid_cnt) || + ((maudcnt > call->opt.aud_cnt || mvidcnt > call->opt.vid_cnt || + mtxtcnt > call->opt.txt_cnt) || (acc->cfg.rtcp_fb_cfg.cap_count))) { pjmedia_sdp_session *local_sdp_renego = NULL; @@ -4360,17 +4444,20 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, * answer, no media count limitation applied as we didn't know yet * which media would pass the SDP negotiation. */ - if (maudcnt > call->opt.aud_cnt || mvidcnt > call->opt.vid_cnt) + if (maudcnt > call->opt.aud_cnt || mvidcnt > call->opt.vid_cnt || + mtxtcnt > call->opt.txt_cnt) { maudcnt = PJ_MIN(maudcnt, call->opt.aud_cnt); mvidcnt = PJ_MIN(mvidcnt, call->opt.vid_cnt); + mtxtcnt = PJ_MIN(mtxtcnt, call->opt.txt_cnt); for (mi=0; mi < local_sdp_renego->media_count; ++mi) { pjmedia_sdp_media *m = local_sdp_renego->media[mi]; if (m->desc.port == 0 || pj_memchr(maudidx, mi, maudcnt*sizeof(maudidx[0])) || - pj_memchr(mvididx, mi, mvidcnt*sizeof(mvididx[0]))) + pj_memchr(mvididx, mi, mvidcnt*sizeof(mvididx[0])) || + pj_memchr(mtxtidx, mi, mtxtcnt*sizeof(mtxtidx[0]))) { continue; } @@ -4428,6 +4515,11 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, if (status != PJ_SUCCESS) goto on_check_med_status; #endif + } else if (call_med->type==PJMEDIA_TYPE_TEXT) { + status = apply_med_update(call_med, local_sdp, remote_sdp, + &need_renego_sdp); + if (status != PJ_SUCCESS) + goto on_check_med_status; } else { status = PJMEDIA_EUNSUPMEDIATYPE; } diff --git a/pjsip/src/pjsua-lib/pjsua_txt.c b/pjsip/src/pjsua-lib/pjsua_txt.c new file mode 100644 index 0000000000..15de8bcae1 --- /dev/null +++ b/pjsip/src/pjsua-lib/pjsua_txt.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2024-2025 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#if defined(PJSUA_MEDIA_HAS_PJMEDIA) && PJSUA_MEDIA_HAS_PJMEDIA != 0 + +#define THIS_FILE "pjsua_txt.c" + + +/* Send text to remote. */ +PJ_DEF(pj_status_t) +pjsua_call_send_text(pjsua_call_id call_id, + const pjsua_call_send_text_param *param) +{ + pjsua_call *call; + pjsua_call_media *call_med; + pjsip_dialog *dlg = NULL; + int med_idx; + pj_status_t status; + + PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls && + param, PJ_EINVAL); + + pj_log_push_indent(); + + status = acquire_call("pjsua_call_send_text()", call_id, &call, &dlg); + if (status != PJ_SUCCESS) + goto on_return; + + /* Verify and normalize media index */ + if ((med_idx = param->med_idx) == -1) { + unsigned i; + + for (i = 0; i < call->med_cnt; ++i) { + if (call->media[i].type == PJMEDIA_TYPE_TEXT && + (call->media[i].dir & PJMEDIA_DIR_ENCODING)) + { + med_idx = i; + break; + } + } + + if (med_idx == -1) { + status = PJ_ENOTFOUND; + goto on_return; + } + } + + PJ_ASSERT_ON_FAIL(med_idx >= 0 && med_idx < (int)call->med_cnt, + {status = PJ_EINVAL; goto on_return;}); + + /* Verify if the stream is transmitting text */ + call_med = &call->media[med_idx]; + if (call_med->type != PJMEDIA_TYPE_TEXT || + (call_med->dir & PJMEDIA_DIR_ENCODING) == 0 || + !call_med->strm.t.stream) + { + status = PJ_EINVALIDOP; + goto on_return; + } + + status = pjmedia_txt_stream_send_text(call_med->strm.t.stream, + ¶m->text); + +on_return: + if (dlg) pjsip_dlg_dec_lock(dlg); + pj_log_pop_indent(); + return status; +} + +/* + * Incoming text callback from the stream. + */ +static void rx_text_cb(pjmedia_txt_stream *strm, void *user_data, + const pjmedia_txt_stream_data *data) +{ + pjsua_call_id call_id; + pjsua_txt_stream_data txt; + + PJ_UNUSED_ARG(strm); + + call_id = (pjsua_call_id)(pj_ssize_t)user_data; + if (pjsua_var.calls[call_id].hanging_up) + return; + + pj_log_push_indent(); + + if (pjsua_var.ua_cfg.cb.on_call_rx_text) { + txt.seq = data->seq; + txt.ts = data->ts; + txt.text = data->text; + (*pjsua_var.ua_cfg.cb.on_call_rx_text)(call_id, &txt); + } + + pj_log_pop_indent(); +} + +/* Internal function to stop text stream */ +void pjsua_txt_stop_stream(pjsua_call_media *call_med) +{ + pjmedia_txt_stream *strm = call_med->strm.t.stream; + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)strm; + pjmedia_rtcp_stat stat; + pjmedia_txt_stream_info prev_txt_si; + + pj_assert(call_med->type == PJMEDIA_TYPE_TEXT); + + if (!strm) + return; + + PJ_LOG(4,(THIS_FILE, "Stopping text stream..")); + pj_log_push_indent(); + + pjmedia_txt_stream_get_info(strm, &prev_txt_si); + call_med->prev_local_addr = prev_txt_si.local_addr; + call_med->prev_rem_addr = prev_txt_si.rem_addr; + + pjmedia_stream_common_send_rtcp_bye(c_strm); + + if (pjmedia_stream_common_get_stat(c_strm, &stat) == PJ_SUCCESS) + { + /* Save RTP timestamp & sequence, so when media session is + * restarted, those values will be restored as the initial + * RTP timestamp & sequence of the new media session. So in + * the same call session, RTP timestamp and sequence are + * guaranteed to be contigue. + */ + call_med->rtp_tx_seq_ts_set = 1 | (1 << 1); + call_med->rtp_tx_seq = stat.rtp_tx_last_seq; + call_med->rtp_tx_ts = stat.rtp_tx_last_ts; + } + + pjmedia_txt_stream_destroy(strm); + call_med->strm.t.stream = NULL; + + pj_log_pop_indent(); +} + +pj_status_t pjsua_txt_channel_update(pjsua_call_media *call_med, + pj_pool_t *tmp_pool, + pjmedia_txt_stream_info *si, + const pjmedia_sdp_session *local_sdp, + const pjmedia_sdp_session *remote_sdp) +{ + pjsua_call *call = call_med->call; + unsigned strm_idx = call_med->idx; + pj_status_t status = PJ_SUCCESS; + + PJ_UNUSED_ARG(tmp_pool); + PJ_UNUSED_ARG(local_sdp); + PJ_UNUSED_ARG(remote_sdp); + + PJ_LOG(4,(THIS_FILE,"Text channel update for index %d for call %d...", + call_med->idx, call_med->call->index)); + pj_log_push_indent(); + + si->rtcp_sdes_bye_disabled = pjsua_var.media_cfg.no_rtcp_sdes_bye; + + /* Check if no media is active */ + if (local_sdp->media[strm_idx]->desc.port != 0) { + + /* Optionally, application may modify other stream settings here + * (such as jitter buffer parameters, codec ptime, etc.) + */ + si->jb_init = pjsua_var.media_cfg.jb_init; + si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre; + si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre; + si->jb_max = pjsua_var.media_cfg.jb_max; + si->jb_discard_algo = pjsua_var.media_cfg.jb_discard_algo; + + /* Set SSRC and CNAME */ + si->ssrc = call_med->ssrc; + si->cname = call->cname; + + /* Set RTP timestamp & sequence, normally these value are intialized + * automatically when stream session created, but for some cases (e.g: + * call reinvite, call update) timestamp and sequence need to be kept + * contigue. + */ + si->rtp_ts = call_med->rtp_tx_ts; + si->rtp_seq = call_med->rtp_tx_seq; + si->rtp_seq_ts_set = call_med->rtp_tx_seq_ts_set; + +#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 + /* Enable/disable stream keep-alive and NAT hole punch. */ + si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka; + + si->ka_cfg = pjsua_var.acc[call->acc_id].cfg.stream_ka_cfg; +#endif + + if (!call->hanging_up && pjsua_var.ua_cfg.cb.on_stream_precreate) { + pjsua_on_stream_precreate_param prm; + prm.stream_idx = strm_idx; + prm.stream_info.type = PJMEDIA_TYPE_TEXT; + prm.stream_info.info.txt = *si; + (*pjsua_var.ua_cfg.cb.on_stream_precreate)(call->index, &prm); + + /* Copy back only the fields which are allowed to be changed. */ + si->jb_init = prm.stream_info.info.aud.jb_init; + si->jb_min_pre = prm.stream_info.info.aud.jb_min_pre; + si->jb_max_pre = prm.stream_info.info.aud.jb_max_pre; + si->jb_max = prm.stream_info.info.aud.jb_max; + si->jb_discard_algo = prm.stream_info.info.aud.jb_discard_algo; +#if defined(PJMEDIA_STREAM_ENABLE_KA) && (PJMEDIA_STREAM_ENABLE_KA != 0) + si->use_ka = prm.stream_info.info.aud.use_ka; +#endif + si->rtcp_sdes_bye_disabled = + prm.stream_info.info.aud.rtcp_sdes_bye_disabled; + } + + /* Create session based on session info. */ + status = pjmedia_txt_stream_create(pjsua_var.med_endpt, NULL, si, + call_med->tp, NULL, + &call_med->strm.t.stream); + if (status != PJ_SUCCESS) { + goto on_return; + } + + /* Start stream. */ + status = pjmedia_txt_stream_start(call_med->strm.t.stream); + if (status != PJ_SUCCESS) { + goto on_return; + } + + if (call_med->prev_state == PJSUA_CALL_MEDIA_NONE) { + pjmedia_stream_common_send_rtcp_sdes( + (pjmedia_stream_common *)call_med->strm.t.stream); + } + + /* Set incoming text callback. */ + if (!call->hanging_up && pjsua_var.ua_cfg.cb.on_call_rx_text) { + pjmedia_txt_stream_set_rx_callback( + call_med->strm.t.stream, &rx_text_cb, + (void *)(pj_ssize_t)(call->index), 0); + } + } + +on_return: + pj_log_pop_indent(); + if (status != PJ_SUCCESS) + pjsua_perror(THIS_FILE, "pjsua_txt_channel_update failed", status); + return status; +} + +#endif /* PJSUA_MEDIA_HAS_PJMEDIA */ diff --git a/pjsip/src/pjsua2/account.cpp b/pjsip/src/pjsua2/account.cpp index 6af262e6c9..1faa97f8dc 100644 --- a/pjsip/src/pjsua2/account.cpp +++ b/pjsip/src/pjsua2/account.cpp @@ -543,6 +543,23 @@ void AccountVideoConfig::writeObject(ContainerNode &node) const } /////////////////////////////////////////////////////////////////////////////// +void AccountTextConfig::readObject(const ContainerNode &node) + PJSUA2_THROW(Error) +{ + ContainerNode this_node = node.readContainer("AccountTextConfig"); + + NODE_READ_INT ( this_node, redundancyLevel); +} + +void AccountTextConfig::writeObject(ContainerNode &node) const + PJSUA2_THROW(Error) +{ + ContainerNode this_node = node.writeNewContainer("AccountTextConfig"); + + NODE_WRITE_INT ( this_node, redundancyLevel); +} +/////////////////////////////////////////////////////////////////////////////// + void AccountIpChangeConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error) { @@ -729,6 +746,9 @@ void AccountConfig::toPj(pjsua_acc_config &ret) const ret.vid_stream_sk_cfg.count = videoConfig.startKeyframeCount; ret.vid_stream_sk_cfg.interval = videoConfig.startKeyframeInterval; + // AccountTextConfig + ret.txt_red_level = textConfig.redundancyLevel; + // AccountIpChangeConfig ret.ip_change_cfg.shutdown_tp = ipChangeConfig.shutdownTp; ret.ip_change_cfg.hangup_calls = ipChangeConfig.hangupCalls; @@ -930,6 +950,9 @@ void AccountConfig::fromPj(const pjsua_acc_config &prm, videoConfig.startKeyframeCount = prm.vid_stream_sk_cfg.count; videoConfig.startKeyframeInterval = prm.vid_stream_sk_cfg.interval; + // AccountTextConfig + textConfig.redundancyLevel = prm.txt_red_level; + // AccountIpChangeConfig ipChangeConfig.shutdownTp = PJ2BOOL(prm.ip_change_cfg.shutdown_tp); ipChangeConfig.hangupCalls = PJ2BOOL(prm.ip_change_cfg.hangup_calls); diff --git a/pjsip/src/pjsua2/call.cpp b/pjsip/src/pjsua2/call.cpp index cc23b27796..1049d0fd95 100644 --- a/pjsip/src/pjsua2/call.cpp +++ b/pjsip/src/pjsua2/call.cpp @@ -204,6 +204,11 @@ void CallSendDtmfParam::fromPj(const pjsua_call_send_dtmf_param ¶m) this->digits = pj2Str(param.digits); } +CallSendTextParam::CallSendTextParam() +{ + medIdx = -1; +} + CallSetting::CallSetting(bool useDefaultValues) { if (useDefaultValues) { @@ -216,13 +221,14 @@ CallSetting::CallSetting(bool useDefaultValues) reqKeyframeMethod = 0; audioCount = 0; videoCount = 0; + textCount = 0; } } bool CallSetting::isEmpty() const { return (flag == 0 && reqKeyframeMethod == 0 && audioCount == 0 && - videoCount == 0); + videoCount == 0 && textCount == 0); } void CallSetting::fromPj(const pjsua_call_setting &prm) @@ -233,6 +239,7 @@ void CallSetting::fromPj(const pjsua_call_setting &prm) this->reqKeyframeMethod = prm.req_keyframe_method; this->audioCount = prm.aud_cnt; this->videoCount = prm.vid_cnt; + this->textCount = prm.txt_cnt; this->mediaDir.clear(); this->customCallId = pj2Str(prm.custom_call_id); @@ -259,6 +266,7 @@ pjsua_call_setting CallSetting::toPj() const setting.req_keyframe_method = this->reqKeyframeMethod; setting.aud_cnt = this->audioCount; setting.vid_cnt = this->videoCount; + setting.txt_cnt = this->textCount; for (mi = 0; mi < this->mediaDir.size(); mi++) { setting.media_dir[mi] = (pjmedia_dir)this->mediaDir[mi]; } @@ -320,6 +328,7 @@ void CallInfo::fromPj(const pjsua_call_info &pci) remOfferer = PJ2BOOL(pci.rem_offerer); remAudioCount = pci.rem_aud_cnt; remVideoCount = pci.rem_vid_cnt; + remTextCount = pci.rem_txt_cnt; for (mi = 0; mi < pci.media_cnt; mi++) { CallMediaInfo med; @@ -384,6 +393,26 @@ void StreamInfo::fromPj(const pjsua_stream_info &info) useKa = PJ2BOOL(info.info.vid.use_ka); #endif rtcpSdesByeDisabled = PJ2BOOL(info.info.vid.rtcp_sdes_bye_disabled); + } else if (type == PJMEDIA_TYPE_TEXT) { + proto = info.info.txt.proto; + dir = info.info.txt.dir; + pj_sockaddr_print(&info.info.txt.rem_addr, straddr, sizeof(straddr), 3); + remoteRtpAddress = straddr; + pj_sockaddr_print(&info.info.txt.rem_rtcp, straddr, sizeof(straddr), 3); + remoteRtcpAddress = straddr; + txPt = info.info.txt.tx_pt; + rxPt = info.info.txt.rx_pt; + codecName = pj2Str(info.info.txt.fmt.encoding_name); + codecClockRate = info.info.txt.fmt.clock_rate; + jbInit = info.info.txt.jb_init; + jbMinPre = info.info.txt.jb_min_pre; + jbMaxPre = info.info.txt.jb_max_pre; + jbMax = info.info.txt.jb_max; + jbDiscardAlgo = PJMEDIA_JB_DISCARD_NONE; +#if defined(PJMEDIA_STREAM_ENABLE_KA) && (PJMEDIA_STREAM_ENABLE_KA != 0) + useKa = PJ2BOOL(info.info.txt.use_ka); +#endif + rtcpSdesByeDisabled = PJ2BOOL(info.info.txt.rtcp_sdes_bye_disabled); } } @@ -393,6 +422,13 @@ void StreamStat::fromPj(const pjsua_stream_stat &prm) jbuf.fromPj(prm.jbuf); } +void OnCallRxTextParam::fromPj(const pjsua_txt_stream_data &prm) +{ + seq = prm.seq; + ts = prm.ts; + text = pj2Str(prm.text); +} + /////////////////////////////////////////////////////////////////////////////// struct call_param @@ -802,6 +838,15 @@ void Call::sendDtmf(const CallSendDtmfParam ¶m) PJSUA2_THROW(Error) PJSUA2_CHECK_EXPR(pjsua_call_send_dtmf(id, &pj_param)); } +void Call::sendText(const CallSendTextParam ¶m) PJSUA2_THROW(Error) +{ + pjsua_call_send_text_param pj_param; + + pjsua_call_send_text_param_default(&pj_param); + pj_param.text = str2Pj(param.text); + + PJSUA2_CHECK_EXPR(pjsua_call_send_text(id, &pj_param)); +} void Call::sendInstantMessage(const SendInstantMessageParam& prm) PJSUA2_THROW(Error) diff --git a/pjsip/src/pjsua2/endpoint.cpp b/pjsip/src/pjsua2/endpoint.cpp index 33495d0213..ae3d799226 100644 --- a/pjsip/src/pjsua2/endpoint.cpp +++ b/pjsip/src/pjsua2/endpoint.cpp @@ -1453,6 +1453,38 @@ void Endpoint::on_dtmf_event(pjsua_call_id call_id, Endpoint::instance().utilAddPendingJob(job); } +struct PendingOnCallRxTextCallback : public PendingJob +{ + int call_id; + OnCallRxTextParam prm; + + virtual void execute(bool is_pending) + { + PJ_UNUSED_ARG(is_pending); + + Call *call = Call::lookup(call_id); + if (!call) + return; + + call->onCallRxText(prm); + } +}; + +void Endpoint::on_call_rx_text(pjsua_call_id call_id, + const pjsua_txt_stream_data *data) +{ + Call *call = Call::lookup(call_id); + if (!call) { + return; + } + + PendingOnCallRxTextCallback *job = new PendingOnCallRxTextCallback; + job->call_id = call_id; + job->prm.fromPj(*data); + + Endpoint::instance().utilAddPendingJob(job); +} + void Endpoint::on_call_transfer_request2(pjsua_call_id call_id, const pj_str_t *dst, pjsip_status_code *code, @@ -2004,6 +2036,7 @@ void Endpoint::libInit(const EpConfig &prmEpConfig) PJSUA2_THROW(Error) //ua_cfg.cb.on_dtmf_digit = &Endpoint::on_dtmf_digit; //ua_cfg.cb.on_dtmf_digit2 = &Endpoint::on_dtmf_digit2; ua_cfg.cb.on_dtmf_event = &Endpoint::on_dtmf_event; + ua_cfg.cb.on_call_rx_text = &Endpoint::on_call_rx_text; ua_cfg.cb.on_call_transfer_request2 = &Endpoint::on_call_transfer_request2; ua_cfg.cb.on_call_transfer_status = &Endpoint::on_call_transfer_status; ua_cfg.cb.on_call_replace_request2 = &Endpoint::on_call_replace_request2; From fde347044e9be073df6397a5921b4239112155c6 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Wed, 26 Mar 2025 12:08:36 +0700 Subject: [PATCH 228/491] Update configure-android to use 'grep' if 'jq' is not available (#4370) --- configure-android | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/configure-android b/configure-android index cca6ce8abd..6d4e540849 100755 --- a/configure-android +++ b/configure-android @@ -47,7 +47,11 @@ if test "x$APP_PLATFORM" = "x"; then if test -d ${ANDROID_NDK_ROOT}/platforms; then APP_PLATFORM=`ls ${ANDROID_NDK_ROOT}/platforms/ | sed 's/android-//' | sort -gr | head -1` elif test -f ${ANDROID_NDK_ROOT}/meta/platforms.json; then - APP_PLATFORM=`cat ${ANDROID_NDK_ROOT}/meta/platforms.json | jq -r ".min"` + if ! [ -x "$(command -v jq)" ]; then + APP_PLATFORM=`cat ${ANDROID_NDK_ROOT}/meta/platforms.json | grep -o '"min":[[:space:]]*[[:digit:]]*' | head -1 | grep -o '[[:digit:]]*' ` + else + APP_PLATFORM=`cat ${ANDROID_NDK_ROOT}/meta/platforms.json | jq -r ".min"` + fi fi if test "x$APP_PLATFORM" != "x"; then APP_PLATFORM="android-${APP_PLATFORM}" From d22292a08e986d5a8f48b6d2feaa4ac1be365c01 Mon Sep 17 00:00:00 2001 From: sauwming Date: Thu, 27 Mar 2025 12:14:21 +0800 Subject: [PATCH 229/491] Fixed text stream Coverity warnings (#4374) --- pjmedia/src/pjmedia/txt_stream.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pjmedia/src/pjmedia/txt_stream.c b/pjmedia/src/pjmedia/txt_stream.c index 60b3f0c78b..fda8d4a0d8 100755 --- a/pjmedia/src/pjmedia/txt_stream.c +++ b/pjmedia/src/pjmedia/txt_stream.c @@ -938,7 +938,6 @@ static pj_status_t get_codec_info(pjmedia_txt_stream_info *si, if (!pj_isdigit(*local_m->desc.fmt[fmti].ptr)) return PJMEDIA_EINVALIDPT; - pt = pj_strtoul(&local_m->desc.fmt[fmti]); attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); @@ -1006,10 +1005,6 @@ static pj_status_t get_codec_info(pjmedia_txt_stream_info *si, pjmedia_stream_info_parse_fmtp(pool, local_m, si->rx_pt, &si->dec_fmtp); - - if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE) - return status; - return PJ_SUCCESS; } From 501ec300e4dd6b47b38921fbed1bb71db4583fac Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 27 Mar 2025 15:09:39 +0700 Subject: [PATCH 230/491] Misc fixes for VS2005 (#4375) --- pjlib/build/pjlib_test.vcproj | 4 ++++ pjlib/src/pjlib-test/pool.c | 4 ++-- pjsip/src/pjsip/sip_auth_client.c | 7 +++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pjlib/build/pjlib_test.vcproj b/pjlib/build/pjlib_test.vcproj index 1196a15823..0c32eb6510 100644 --- a/pjlib/build/pjlib_test.vcproj +++ b/pjlib/build/pjlib_test.vcproj @@ -3930,6 +3930,10 @@ RelativePath="..\src\pjlib-test\hash_test.c" > + + diff --git a/pjlib/src/pjlib-test/pool.c b/pjlib/src/pjlib-test/pool.c index c2aac396e4..0a079bffb3 100644 --- a/pjlib/src/pjlib-test/pool.c +++ b/pjlib/src/pjlib-test/pool.c @@ -306,6 +306,7 @@ int pool_test(void) { enum { LOOP = 2 }; int rc; + int loop; #if PJ_HAS_POOL_ALT_API == 0 rc = capacity_test(); @@ -319,7 +320,6 @@ int pool_test(void) if (rc) return rc; #if PJ_HAS_POOL_ALT_API == 0 - int loop; for (loop=0; loopparent) { pj_lock_acquire(sess->parent->lock); pauth = find_cached_auth(sess->parent, realm, algorithm_type); @@ -737,11 +738,13 @@ static pjsip_cached_auth *find_cached_auth( pjsip_auth_clt_sess *sess, return pauth; } - pjsip_cached_auth *auth = sess->cached_auth.next; + auth = sess->cached_auth.next; while (auth != &sess->cached_auth) { if (pj_stricmp(&auth->realm, realm) == 0 && auth->challenge_algorithm_type == algorithm_type) + { return auth; + } auth = auth->next; } From 078bfeae295464d36e9756f187e2f1b76bf2d350 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 27 Mar 2025 15:28:01 +0700 Subject: [PATCH 231/491] Implement audio video synchronization (#4325) --- pjmedia/build/Makefile | 2 +- pjmedia/build/pjmedia.vcproj | 8 + pjmedia/build/pjmedia.vcxproj | 2 + pjmedia/build/pjmedia.vcxproj.filters | 6 + pjmedia/include/pjmedia/av_sync.h | 256 +++++++++++++ pjmedia/include/pjmedia/avi_stream.h | 17 +- pjmedia/include/pjmedia/config.h | 26 ++ pjmedia/include/pjmedia/jbuf.h | 15 + pjmedia/include/pjmedia/rtcp.h | 4 + pjmedia/include/pjmedia/stream_common.h | 25 +- pjmedia/src/pjmedia/av_sync.c | 469 +++++++++++++++++++++++ pjmedia/src/pjmedia/avi_player.c | 435 +++++++++++++++++---- pjmedia/src/pjmedia/jbuf.c | 40 +- pjmedia/src/pjmedia/rtcp.c | 19 +- pjmedia/src/pjmedia/stream.c | 103 ++++- pjmedia/src/pjmedia/stream_common.c | 43 +++ pjmedia/src/pjmedia/stream_imp_common.c | 12 + pjmedia/src/pjmedia/vid_stream.c | 119 +++++- pjsip-apps/src/swig/symbols.i | 6 +- pjsip/include/pjsua-lib/pjsua.h | 7 +- pjsip/include/pjsua-lib/pjsua_internal.h | 2 + pjsip/src/pjsua-lib/pjsua_aud.c | 9 + pjsip/src/pjsua-lib/pjsua_media.c | 34 ++ pjsip/src/pjsua-lib/pjsua_vid.c | 9 + 24 files changed, 1560 insertions(+), 108 deletions(-) create mode 100644 pjmedia/include/pjmedia/av_sync.h create mode 100644 pjmedia/src/pjmedia/av_sync.c diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile index 57b1d0a1c1..2b9b9af02e 100644 --- a/pjmedia/build/Makefile +++ b/pjmedia/build/Makefile @@ -58,7 +58,7 @@ export _LDFLAGS := $(APP_THIRD_PARTY_LIBS) \ # export PJMEDIA_SRCDIR = ../src/pjmedia export PJMEDIA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ - alaw_ulaw.o alaw_ulaw_table.o avi_player.o \ + alaw_ulaw.o alaw_ulaw_table.o avi_player.o av_sync.o \ bidirectional.o clock_thread.o codec.o conference.o \ conf_switch.o converter.o converter_libswscale.o converter_libyuv.o \ delaybuf.o echo_common.o \ diff --git a/pjmedia/build/pjmedia.vcproj b/pjmedia/build/pjmedia.vcproj index ab3f97d0d5..3a28d64fea 100644 --- a/pjmedia/build/pjmedia.vcproj +++ b/pjmedia/build/pjmedia.vcproj @@ -3342,6 +3342,10 @@ RelativePath="..\src\pjmedia\audiodev.c" > + + @@ -7599,6 +7603,10 @@ RelativePath="..\include\pjmedia\audiodev.h" > + + diff --git a/pjmedia/build/pjmedia.vcxproj b/pjmedia/build/pjmedia.vcxproj index 1db180fadc..b8aed5ff71 100644 --- a/pjmedia/build/pjmedia.vcxproj +++ b/pjmedia/build/pjmedia.vcxproj @@ -610,6 +610,7 @@ + @@ -765,6 +766,7 @@ + diff --git a/pjmedia/build/pjmedia.vcxproj.filters b/pjmedia/build/pjmedia.vcxproj.filters index 8316c1cd49..4aa545eafa 100644 --- a/pjmedia/build/pjmedia.vcxproj.filters +++ b/pjmedia/build/pjmedia.vcxproj.filters @@ -233,6 +233,9 @@ Source Files + + Source Files + @@ -424,5 +427,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/pjmedia/include/pjmedia/av_sync.h b/pjmedia/include/pjmedia/av_sync.h new file mode 100644 index 0000000000..5324a70f36 --- /dev/null +++ b/pjmedia/include/pjmedia/av_sync.h @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2025 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJMEDIA_AV_SYNC_H__ +#define __PJMEDIA_AV_SYNC_H__ + + /** + * @file av_sync.h + * @brief Inter-media Synchronization. + */ +#include + + +PJ_BEGIN_DECL + + +/** + * @defgroup PJMEDIA_AV_SYNC Inter-media Synchronization + * @ingroup PJMEDIA_SESSION + * @brief Synchronize presentation time of multiple media in a session. + * @{ + * + * A call session may consist of multiple media, e.g: some audio and some + * video, which frequently have different delays when presented in the + * receiver side. This module synchronizes all media in the same session + * based on NTP timestamp & RTP timestamp info provided by the sender in + * RTCP SR. + * + * Here are steps to use this module: + * 1. Create AV sync using #pjmedia_av_sync_create(). + * 2. Adds all media to be synchronized using #pjmedia_av_sync_add_media(). + * 3. Call #pjmedia_av_sync_update_ref() each time the media receiving + * an RTCP SR packet. + * 4. Call #pjmedia_av_sync_update_pts() each time the media returning + * a frame to be presented, e.g: via port.get_frame(). The function may + * request the media to adjust its delay. + * 5. Call #pjmedia_av_sync_del_media() when a media is removed from the + * session. + * 6. Call #pjmedia_av_sync_destroy() when the session is ended. + * + * The primary synchronization logic is implemented within the + * #pjmedia_av_sync_update_pts() function. This function will calculate + * the lag between the calling media to the earliest media and will provide + * a feedback to the calling media whether it is in synchronized state, + * late, or early so the media can respond accordingly. + * Initially this function will try to request slower media to speed up. + * If after a specific number of requests (i.e: configurable via + * PJMEDIA_AVSYNC_MAX_SPEEDUP_REQ_CNT) and the lag is still beyond a tolerable + * value (i.e: configurable via PJMEDIA_AVSYNC_MAX_TOLERABLE_LAG_MSEC), the + * function will issue slow down request to the fastest media. + */ + + +/** + * Inter-media synchronizer, opaque. + */ +typedef struct pjmedia_av_sync pjmedia_av_sync; + + +/** + * Media synchronization handle, opaque. + */ +typedef struct pjmedia_av_sync_media pjmedia_av_sync_media; + + +/** + * Synchronizer settings. + */ +typedef struct { + /** + * Name of the syncrhonizer + */ + char *name; + + /** + * Streaming mode. If set to PJ_TRUE, the delay adjustment values will + * be smoothened and marked up to prevent possible delay increase on + * all media. + */ + pj_bool_t is_streaming; + +} pjmedia_av_sync_setting; + + +/** + * Media settings. + */ +typedef struct { + /** + * Name of the media + */ + char *name; + + /** + * Media type. + */ + pjmedia_type type; + + /** + * Media clock rate or sampling rate. + */ + unsigned clock_rate; + +} pjmedia_av_sync_media_setting; + + +/** + * Get default settings for synchronizer. + * + * @param setting The synchronizer settings. + */ +PJ_DECL(void) pjmedia_av_sync_setting_default( + pjmedia_av_sync_setting *setting); + +/** + * Get default settings for media. + * + * @param setting The media settings. + */ +PJ_DECL(void) pjmedia_av_sync_media_setting_default( + pjmedia_av_sync_media_setting *setting); + +/** + * Create media synchronizer. + * + * @param pool The memory pool. + * @param option The synchronizer settings. + * @param av_sync The pointer to receive the media synchronizer. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_av_sync_create( + pj_pool_t *pool, + const pjmedia_av_sync_setting *setting, + pjmedia_av_sync **av_sync); + + +/** + * Destroy media synchronizer. + * + * @param av_sync The media synchronizer. + */ +PJ_DECL(void) pjmedia_av_sync_destroy(pjmedia_av_sync *av_sync); + + +/** + * Reset synchronization states. Any existing media will NOT be removed, + * but their states will be reset. + * + * @param av_sync The media synchronizer. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_av_sync_reset(pjmedia_av_sync *av_sync); + + +/** + * Add a media to synchronizer. + * + * @param av_sync The media synchronizer. + * @param setting The media settings. + * @param av_sync_media The pointer to receive the media synchronization + * handle. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_av_sync_add_media( + pjmedia_av_sync* av_sync, + const pjmedia_av_sync_media_setting *setting, + pjmedia_av_sync_media **av_sync_media); + + +/** + * Remove a media from synchronizer. + * + * @param av_sync The media synchronizer. + * @param av_sync_media The media synchronization handle. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_av_sync_del_media( + pjmedia_av_sync *av_sync, + pjmedia_av_sync_media *av_sync_media); + + +/** + * Update synchronizer about the last presentation timestamp of the specified + * media. Normally this function is called each time the media produces + * a frame to be rendered (e.g: in port's get_frame() method). Upon returning, + * the media may be requested to adjust its delay so it matches to the + * earliest or the latest media, i.e: by speeding up or slowing down. + * + * Initially this function will try to request slower media to speed up. + * If after a specific number of requests (i.e: configurable via + * PJMEDIA_AVSYNC_MAX_SPEEDUP_REQ_CNT) and the lag is still beyond a tolerable + * value (i.e: configurable via PJMEDIA_AVSYNC_MAX_TOLERABLE_LAG_MSEC), the + * function will issue slow down request to the fastest media. + * + * @param av_sync_media The media synchronization handle. + * @param pts The presentation timestamp. + * @param adjust_delay Optional pointer to receive adjustment delay + * required, in milliseconds, to make this media + * synchronized to the fastest media. + * Possible output values are: + * 0 when no action is needed, + * possitive value when increasing delay is needed, + * or negative value when decreasing delay is needed. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_av_sync_update_pts( + pjmedia_av_sync_media *av_sync_media, + const pj_timestamp *pts, + pj_int32_t *adjust_delay); + + +/** + * Update synchronizer about reference timestamps of the specified media. + * Normally this function is called each time the media receives RTCP SR + * packet. + * + * @param av_sync_media The media synchronization handle. + * @param ntp The NTP timestamp info from RTCP SR. + * @param ts The RTP timestamp info from RTCP SR. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_av_sync_update_ref( + pjmedia_av_sync_media *av_sync_media, + const pj_timestamp *ntp, + const pj_timestamp *ts); + + +/** + * @} + */ + + +PJ_END_DECL + + +#endif /* __PJMEDIA_AV_SYNC_H__ */ diff --git a/pjmedia/include/pjmedia/avi_stream.h b/pjmedia/include/pjmedia/avi_stream.h index 17839b469e..ada43eb77f 100644 --- a/pjmedia/include/pjmedia/avi_stream.h +++ b/pjmedia/include/pjmedia/avi_stream.h @@ -45,7 +45,13 @@ enum pjmedia_avi_file_player_option * Tell the file player to return NULL frame when the whole * file has been played. */ - PJMEDIA_AVI_FILE_NO_LOOP = 1 + PJMEDIA_AVI_FILE_NO_LOOP = 1, + + /** + * Set the file player to permit independent playback of audio and + * video streams without synchronization. + */ + PJMEDIA_AVI_FILE_NO_SYNC = 2 }; /** @@ -64,9 +70,16 @@ typedef struct pjmedia_avi_streams pjmedia_avi_streams; * reading AVI file with uncompressed video format and * 16 bit PCM or compressed G.711 A-law/U-law audio format. * + * By default, avi streams will loop the file playback and synchronize + * audio and video streams. To change this behavior, use the flags parameter. + * + * When synchronization is enabled, the file player will wait for all + * media streams to reach the end of file before rewinding the file. + * * @param pool Pool to create the streams. * @param filename File name to open. - * @param flags Avi streams creation flags. + * @param flags Avi streams creation flags, bitmask combination of + * #pjmedia_avi_file_player_option. * @param p_streams Pointer to receive the avi streams instance. * * @return PJ_SUCCESS on success. diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h index 24469af4a4..a13204aa2a 100644 --- a/pjmedia/include/pjmedia/config.h +++ b/pjmedia/include/pjmedia/config.h @@ -1765,6 +1765,32 @@ #undef PJMEDIA_VID_STREAM_CHECK_RTP_PT #define PJMEDIA_VID_STREAM_CHECK_RTP_PT PJMEDIA_STREAM_CHECK_RTP_PT + +/** + * Maximum tolerable presentation lag from the earliest to the latest media, + * in milliseconds, in inter-media synchronization. When the delay is + * higher than this setting, the media synchronizer will request the slower + * media to speed up. And if after a number of speed up requests the delay + * is still beyond this setting, the fastest media will be requested to + * slow down. + * + * Default: 45 ms + */ +#ifndef PJMEDIA_AVSYNC_MAX_TOLERABLE_LAG_MSEC +# define PJMEDIA_AVSYNC_MAX_TOLERABLE_LAG_MSEC 45 +#endif + + +/** + * Maximum number of speed up request to synchronize presentation time, + * before a slow down request to the fastest media is issued. + * + * Default: 10 + */ +#ifndef PJMEDIA_AVSYNC_MAX_SPEEDUP_REQ_CNT +# define PJMEDIA_AVSYNC_MAX_SPEEDUP_REQ_CNT 10 +#endif + /** * @} */ diff --git a/pjmedia/include/pjmedia/jbuf.h b/pjmedia/include/pjmedia/jbuf.h index 6f727494de..9f09af051e 100644 --- a/pjmedia/include/pjmedia/jbuf.h +++ b/pjmedia/include/pjmedia/jbuf.h @@ -102,6 +102,7 @@ typedef struct pjmedia_jb_state unsigned min_prefetch; /**< Minimum allowed prefetch, in frms. */ unsigned max_prefetch; /**< Maximum allowed prefetch, in frms. */ unsigned max_count; /**< Jitter buffer capacity, in frames. */ + unsigned min_delay_set; /**< Minimum delay setting, in frames. */ /* Status */ unsigned burst; /**< Current burst level, in frames */ @@ -474,6 +475,20 @@ PJ_DECL(pj_status_t) pjmedia_jbuf_get_state( const pjmedia_jbuf *jb, pjmedia_jb_state *state ); +/** + * Set minimum delay of the jitter buffer. Normally jitter buffer tries to + * maintain the optimal delay calculated based on current burst level. + * When the minimum delay is set, the jitter buffer will adjust the delay to + * the greater of the optimal delay and the minimum delay. + * + * @param jb The jitter buffer. + * @param min_delay The minimum delay, in millisecond. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_jbuf_set_min_delay(pjmedia_jbuf *jb, + unsigned min_delay); + PJ_END_DECL diff --git a/pjmedia/include/pjmedia/rtcp.h b/pjmedia/include/pjmedia/rtcp.h index d9ab062c45..5e9897fa24 100644 --- a/pjmedia/include/pjmedia/rtcp.h +++ b/pjmedia/include/pjmedia/rtcp.h @@ -263,6 +263,10 @@ typedef struct pjmedia_rtcp_session pj_uint32_t rx_lsr; /**< NTP ts in last SR received */ pj_timestamp rx_lsr_time;/**< Time when last SR is received */ + pj_uint32_t rx_lsr_ts; /**< RTP ts in last SR received */ + pj_timestamp rx_lsr_ntp; /**< Original/64bit NTP ts in last + SR received */ + pj_uint32_t peer_ssrc; /**< Peer SSRC */ pjmedia_rtcp_stat stat; /**< Bidirectional stream stat. */ diff --git a/pjmedia/include/pjmedia/stream_common.h b/pjmedia/include/pjmedia/stream_common.h index b786929ace..10516dbbe9 100644 --- a/pjmedia/include/pjmedia/stream_common.h +++ b/pjmedia/include/pjmedia/stream_common.h @@ -24,6 +24,7 @@ * @brief Stream common functions. */ +#include #include #include #include @@ -151,6 +152,11 @@ typedef struct pjmedia_stream_common int pending_rtcp_fb_nack; /**< Any pending NACK? */ pjmedia_rtcp_fb_nack rtcp_fb_nack; /**< TX NACK state. */ int rtcp_fb_nack_cap_idx; /**< RX NACK cap idx. */ + + /* Media synchronization */ + pjmedia_av_sync *av_sync; /**< Media sync. */ + pjmedia_av_sync_media *av_sync_media; /**< Media sync media */ + } pjmedia_stream_common; @@ -276,7 +282,6 @@ pjmedia_stream_common_send_rtcp_bye( pjmedia_stream_common *stream ); * and generally it is not advisable for app to modify them. * * @param stream The media stream. - * * @param session_info The stream session info. * * @return PJ_SUCCESS on success. @@ -286,6 +291,24 @@ pjmedia_stream_common_get_rtp_session_info(pjmedia_stream_common *stream, pjmedia_stream_rtp_sess_info *session_info); +/** + * Set or reset media presentation synchronizer. The synchronizer manages + * presentation time of media streams in the session, e.g: audio & video. + * + * Application creates a media synchronizer and assign it to all media streams + * whose presentation time to be synchronized using this function. + * + * @param stream The media stream. + * @param av_sync The media presentation synchronizer, or NULL to + * remove this stream from current synchronizer. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_stream_common_set_avsync(pjmedia_stream_common* stream, + pjmedia_av_sync* av_sync); + + /* Internal function. */ /* Internal: * Send RTCP SDES for the media stream. */ diff --git a/pjmedia/src/pjmedia/av_sync.c b/pjmedia/src/pjmedia/av_sync.c new file mode 100644 index 0000000000..30dff51559 --- /dev/null +++ b/pjmedia/src/pjmedia/av_sync.c @@ -0,0 +1,469 @@ +/* + * Copyright (C) 2025 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +/* Enable/disable trace */ +#if 0 +# define TRACE_(x) PJ_LOG(5, x) +#else +# define TRACE_(x) +#endif + +/* AV sync media */ +struct pjmedia_av_sync_media +{ + PJ_DECL_LIST_MEMBER(struct pjmedia_av_sync_media); + + pjmedia_av_sync *av_sync; /* The AV sync instance */ + pjmedia_av_sync_media_setting setting; /* Media settings */ + + /* Reference timestamp */ + pj_bool_t is_ref_set; /* Has reference been set? */ + pj_timestamp ref_ts; /* Ref ts, in sample units */ + pj_timestamp ref_ntp; /* Ref ts, in NTP units */ + + /* Last presentation timestamp */ + pj_timestamp last_ntp; /* Last PTS, in NTP units */ + pj_int32_t smooth_diff; + + /* Delay adjustment requested to this media */ + pj_int32_t last_adj_delay_req; /* Last requested delay */ + unsigned adj_delay_req_cnt; /* Request counter */ +}; + + +/* AV sync */ +struct pjmedia_av_sync +{ + pj_pool_t *pool; + pjmedia_av_sync_media media_list; + pjmedia_av_sync_media free_media_list; + pj_grp_lock_t *grp_lock; + unsigned last_idx; + pjmedia_av_sync_setting setting; + + /* Maximum NTP time of all media */ + pj_timestamp max_ntp; + + /* Some media cannot catch up, request for slow down, in milliseconds */ + unsigned slowdown_req_ms; +}; + + +static void ntp_add_ts(pj_timestamp* ntp, unsigned ts, unsigned clock_rate) +{ + pj_timestamp ts_diff; + + ts_diff.u64 = ((pj_uint64_t)ts << 32) / clock_rate; + pj_add_timestamp(ntp, &ts_diff); +} + +static unsigned ntp_to_ms(const pj_timestamp* ntp) +{ + pj_uint64_t ms; + + ms = ntp->u32.hi * 1000 + (((pj_uint64_t)ntp->u32.lo * 1000) >> 32); + return (unsigned)ms; +} + + +/* AV sync destroy handler. */ +static void avs_on_destroy(void* arg) +{ + pjmedia_av_sync* avs = (pjmedia_av_sync*)arg; + PJ_LOG(4, (avs->setting.name, "%s destroyed", avs->setting.name)); + pj_pool_release(avs->pool); +} + + +/* Get default values for synchronizer settings. */ +PJ_DEF(void) pjmedia_av_sync_setting_default( + pjmedia_av_sync_setting *setting) +{ + pj_bzero(setting, sizeof(*setting)); +} + + +/* Get default values for media settings. */ +PJ_DEF(void) pjmedia_av_sync_media_setting_default( + pjmedia_av_sync_media_setting* setting) +{ + pj_bzero(setting, sizeof(*setting)); +} + + +/* Create media synchronizer. */ +PJ_DEF(pj_status_t) pjmedia_av_sync_create( + pj_pool_t *pool_, + const pjmedia_av_sync_setting *setting, + pjmedia_av_sync **av_sync) +{ + pj_pool_t* pool = NULL; + pjmedia_av_sync* avs = NULL; + pj_status_t status; + + PJ_ASSERT_RETURN(pool_ && av_sync && setting, PJ_EINVAL); + + pool = pj_pool_create(pool_->factory, "avsync%p", 512, 512, NULL); + if (!pool) { + status = PJ_ENOMEM; + goto on_error; + } + + avs = PJ_POOL_ZALLOC_T(pool, pjmedia_av_sync); + if (!avs) { + status = PJ_ENOMEM; + goto on_error; + } + avs->pool = pool; + avs->setting = *setting; + if (setting->name) { + pj_size_t len = PJ_MIN(PJ_MAX_OBJ_NAME, + pj_ansi_strlen(setting->name)+1); + avs->setting.name = pj_pool_zalloc(avs->pool, len); + pj_ansi_snprintf(avs->setting.name, len, "%s", setting->name); + } else { + avs->setting.name = pool->obj_name; + } + + status = pj_grp_lock_create_w_handler(pool, NULL, avs, &avs_on_destroy, + &avs->grp_lock); + if (status != PJ_SUCCESS) + goto on_error; + + pj_grp_lock_add_ref(avs->grp_lock); + pj_list_init(&avs->media_list); + pj_list_init(&avs->free_media_list); + + PJ_LOG(4, (avs->setting.name, "%s created", avs->setting.name)); + *av_sync = avs; + return PJ_SUCCESS; + +on_error: + if (pool) + pj_pool_release(pool); + + return status; +} + + +/* Destroy media synchronizer. */ +PJ_DEF(void) pjmedia_av_sync_destroy(pjmedia_av_sync* avs) +{ + PJ_ASSERT_ON_FAIL(avs, return); + PJ_LOG(4, (avs->setting.name, "%s destroy requested", + avs->setting.name)); + pj_grp_lock_dec_ref(avs->grp_lock); +} + + +/* Reset synchronization states. */ +PJ_DEF(pj_status_t) pjmedia_av_sync_reset(pjmedia_av_sync *avs) +{ + pjmedia_av_sync_media* m; + PJ_ASSERT_RETURN(avs, PJ_EINVAL); + + pj_grp_lock_acquire(avs->grp_lock); + avs->max_ntp.u64 = 0; + avs->slowdown_req_ms = 0; + + m = avs->media_list.next; + while (m != &avs->media_list) { + m->is_ref_set = PJ_FALSE; + m->last_ntp.u64 = 0; + m->last_adj_delay_req = 0; + m->adj_delay_req_cnt = 0; + m->smooth_diff = 0; + m = m->next; + } + pj_grp_lock_release(avs->grp_lock); + return PJ_SUCCESS; +} + + +/* Add media to synchronizer. */ +PJ_DEF(pj_status_t) pjmedia_av_sync_add_media( + pjmedia_av_sync *avs, + const pjmedia_av_sync_media_setting *setting, + pjmedia_av_sync_media **media) +{ + pjmedia_av_sync_media* m; + pj_status_t status = PJ_SUCCESS; + char* m_name; + + PJ_ASSERT_RETURN(avs && media && setting, PJ_EINVAL); + + pj_grp_lock_acquire(avs->grp_lock); + + /* Get media from free list, if any, otherwise allocate a new one */ + if (!pj_list_empty(&avs->free_media_list)) { + m = avs->free_media_list.next; + pj_list_erase(m); + m_name = m->setting.name; + } else { + m = PJ_POOL_ZALLOC_T(avs->pool, pjmedia_av_sync_media); + m_name = pj_pool_zalloc(avs->pool, PJ_MAX_OBJ_NAME); + if (!m || !m_name) { + status = PJ_ENOMEM; + goto on_return; + } + } + + m->av_sync = avs; + m->setting = *setting; + if (setting->name) { + pj_ansi_snprintf(m_name, PJ_MAX_OBJ_NAME, "%s", setting->name); + } else { + pj_ansi_snprintf(m_name, PJ_MAX_OBJ_NAME, "avs_med_%d", + ++avs->last_idx); + } + m->setting.name = m_name; + + pj_list_push_back(&avs->media_list, m); + pj_grp_lock_add_ref(avs->grp_lock); + + *media = m; + PJ_LOG(4, (avs->setting.name, "Added media %s, clock rate=%d", + m->setting.name, m->setting.clock_rate)); + +on_return: + pj_grp_lock_release(avs->grp_lock); + return status; +} + + +/* Remove media from synchronizer. */ +PJ_DEF(pj_status_t) pjmedia_av_sync_del_media( + pjmedia_av_sync *avs, + pjmedia_av_sync_media *media) +{ + PJ_ASSERT_RETURN(media, PJ_EINVAL); + PJ_ASSERT_RETURN(!avs || media->av_sync == avs, PJ_EINVAL); + + if (!avs) + avs = media->av_sync; + + pj_grp_lock_acquire(avs->grp_lock); + pj_list_erase(media); + + /* Zero some fields */ + media->is_ref_set = PJ_FALSE; + media->last_adj_delay_req = 0; + media->adj_delay_req_cnt = 0; + media->smooth_diff = 0; + + pj_list_push_back(&avs->free_media_list, media); + pj_grp_lock_release(avs->grp_lock); + + PJ_LOG(4, (avs->setting.name, "Removed media %s", media->setting.name)); + pj_grp_lock_dec_ref(avs->grp_lock); + + return PJ_SUCCESS; +} + + +/* Update synchronizer about the last timestamp reference of the specified + * media. + */ +PJ_DEF(pj_status_t) pjmedia_av_sync_update_ref( + pjmedia_av_sync_media* media, + const pj_timestamp* ntp, + const pj_timestamp* ts) +{ + PJ_ASSERT_RETURN(media && ntp && ts, PJ_EINVAL); + + media->ref_ntp = *ntp; + media->ref_ts = *ts; + media->is_ref_set = PJ_TRUE; + TRACE_((media->av_sync->setting.name, "%s updates ref ntp=%u ts=%u", + media->setting.name, ntp->u64, ts->u64)); + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pjmedia_av_sync_update_pts( + pjmedia_av_sync_media *media, + const pj_timestamp *pts, + pj_int32_t *adjust_delay) +{ + pjmedia_av_sync *avs; + pj_int32_t diff; + pj_timestamp max_ntp; + + PJ_ASSERT_RETURN(media && media->av_sync && pts, PJ_EINVAL); + + /* Reset the adjustment delay */ + if (adjust_delay) + *adjust_delay = 0; + + /* Make sure we have a reference */ + if (!media->is_ref_set) + return PJ_EINVALIDOP; + + diff = pj_timestamp_diff32(&media->ref_ts, pts); + + /* Only process if pts is increasing */ + if (diff <= 0) + return PJ_ETOOSMALL; + + avs = media->av_sync; + TRACE_((avs->setting.name, "%s updates pts=%u", + media->setting.name, pts->u64)); + + /* Update last presentation time */ + media->last_ntp = media->ref_ntp; + ntp_add_ts(&media->last_ntp, diff, media->setting.clock_rate); + + /* Get NTP timestamp of the earliest media */ + pj_grp_lock_acquire(avs->grp_lock); + max_ntp = avs->max_ntp; + pj_grp_lock_release(avs->grp_lock); + + /* Check if this media is the fastest/earliest */ + if (pj_cmp_timestamp(&media->last_ntp, &max_ntp) > 0) { + /* Yes, it is the fastest, update the max timestamp */ + pj_grp_lock_acquire(avs->grp_lock); + avs->max_ntp = media->last_ntp; + pj_grp_lock_release(avs->grp_lock); + + /* Check if there is any request to slow down */ + if (avs->slowdown_req_ms) { + media->last_adj_delay_req = avs->slowdown_req_ms; + media->adj_delay_req_cnt = 0; + avs->slowdown_req_ms = 0; + TRACE_((avs->setting.name, + "%s is requested to slow down by %dms", + media->setting.name, media->last_adj_delay_req)); + if (adjust_delay) + *adjust_delay = media->last_adj_delay_req; + + return PJ_SUCCESS; + } + } else { + /* Not the fastest. */ + pj_timestamp ntp_diff = max_ntp; + unsigned ms_diff, ms_req; + + /* First, check the lag from the fastest. */ + pj_sub_timestamp(&ntp_diff, &media->last_ntp); + ms_diff = ntp_to_ms(&ntp_diff); + + /* For streaming, smoothen (apply weight of 9 for current lag), + * and round down the lag to the nearest 10. + */ + if (avs->setting.is_streaming) { + ms_diff = ((ms_diff + 9 * media->smooth_diff) / 100) * 10; + media->smooth_diff = ms_diff; + } + + /* The lag is tolerable, just return 0 */ + if (ms_diff <= PJMEDIA_AVSYNC_MAX_TOLERABLE_LAG_MSEC) { + if (media->last_adj_delay_req) { + TRACE_((avs->setting.name, + "%s lag looks good now=%ums", + media->setting.name, ms_diff)); + } + /* Reset the request delay & counter */ + media->adj_delay_req_cnt = 0; + media->last_adj_delay_req = 0; + + return PJ_SUCCESS; + } + + /* Check if any speed-up request has been done before */ + if (media->last_adj_delay_req) { + /* Check if request number has reached limit */ + if (media->adj_delay_req_cnt>=PJMEDIA_AVSYNC_MAX_SPEEDUP_REQ_CNT) + { + + /* After several requests this media still cannot catch up, + * signal the synchronizer to slow down the fastest media. + * + * For streaming mode, request slow down 3/4 of required to + * prevent possible delay increase on all media. + */ + ms_req = ms_diff; + if (avs->setting.is_streaming) + ms_req = ms_req * 3/4; + + if (avs->slowdown_req_ms < ms_req) + avs->slowdown_req_ms = ms_req; + + TRACE_((avs->setting.name, + "%s request limit has been reached, requesting " + "the fastest media to slow down by %ums", + media->setting.name, avs->slowdown_req_ms)); + + /* Reset the request counter. + * And still keep requesting for speed up, shouldn't we? + */ + media->adj_delay_req_cnt = 0; + } else { + pj_int32_t progress, min_expected; + + /* Check if the previous delay request has shown some + * progress. + */ + progress = (-media->last_adj_delay_req) - ms_diff; + min_expected = -media->last_adj_delay_req / + (PJMEDIA_AVSYNC_MAX_SPEEDUP_REQ_CNT - + media->adj_delay_req_cnt + 1); + if (progress >= min_expected) { + /* Yes, let's just request again and wait */ + TRACE_((avs->setting.name, + "%s speeds up in progress, current lag=%ums", + media->setting.name, ms_diff)); + } + } + } else { + /* First request to speed up */ + media->adj_delay_req_cnt = 0; + } + + /* Request the media to speed up & increment the counter. + * + * For streaming mode, request speed-up 4/3 of required to + * prevent possible delay increase on all media. + */ + ms_req = ms_diff; + if (avs->setting.is_streaming) + ms_req = ms_req * 4/3; + + media->last_adj_delay_req = -(pj_int32_t)ms_req; + media->adj_delay_req_cnt++; + + TRACE_((avs->setting.name, + "%s is requested to speed up #%d by %dms", + media->setting.name, media->adj_delay_req_cnt, + -media->last_adj_delay_req)); + + if (adjust_delay) + *adjust_delay = media->last_adj_delay_req; + + return PJ_SUCCESS; + } + + return PJ_SUCCESS; +} diff --git a/pjmedia/src/pjmedia/avi_player.c b/pjmedia/src/pjmedia/avi_player.c index f0fe2e9922..1ea599b10e 100644 --- a/pjmedia/src/pjmedia/avi_player.c +++ b/pjmedia/src/pjmedia/avi_player.c @@ -21,10 +21,12 @@ */ #include #include +#include #include #include #include #include +#include #include #include #include @@ -117,9 +119,13 @@ static avi_fmt_info avi_fmts[] = struct pjmedia_avi_streams { - pj_pool_t *pool; - unsigned num_streams; - pjmedia_port **streams; + pj_pool_t *pool; + unsigned num_streams; + pjmedia_port **streams; + + /* AV synchronization */ + pjmedia_av_sync *avsync; + pj_size_t eof_cnt; }; struct avi_reader_port @@ -128,7 +134,6 @@ struct avi_reader_port unsigned stream_id; unsigned options; pjmedia_format_id fmt_id; - unsigned usec_per_frame; pj_uint16_t bits_per_sample; pj_bool_t eof; pj_off_t fsize; @@ -136,8 +141,15 @@ struct avi_reader_port pj_uint8_t pad; pj_oshandle_t fd; pj_ssize_t size_left; + + pj_size_t frame_cnt; pj_timestamp next_ts; + /* AV synchronization */ + pjmedia_av_sync_media *avsync_media; + pj_size_t slow_down_frm; + pjmedia_avi_streams *avi_streams; + pj_status_t (*cb)(pjmedia_port*, void*); pj_bool_t subscribed; void (*cb2)(pjmedia_port*, void*); @@ -203,10 +215,27 @@ static pj_status_t file_read3(pj_oshandle_t fd, void *data, pj_ssize_t size, static void streams_on_destroy(void *arg) { pjmedia_avi_streams *streams = (pjmedia_avi_streams*)arg; + + if (streams->avsync) + pjmedia_av_sync_destroy(streams->avsync); pj_pool_safe_release(&streams->pool); } +/* Get filename from path */ +static const char *get_fname(const char *path) +{ + pj_size_t len = pj_ansi_strlen(path); + const char *p = path + len - 1; + + while (p > path) { + if (*p == '\\' || *p == '/' || *p == ':') + return p + 1; + --p; + } + return p; +} + /* * Create AVI player port. */ @@ -495,6 +524,7 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool_, for (i = 0; i < nstr; i++) { strl_hdr_t *strl_hdr = &avi_hdr.strl_hdr[fport[i]->stream_id]; + char port_name[PJ_MAX_OBJ_NAME]; /* Initialize */ fport[i]->options = options; @@ -512,7 +542,7 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool_, strl_hdr->codec); fport[i]->bits_per_sample = (vfi ? vfi->bpp : 0); - fport[i]->usec_per_frame = avi_hdr.avih_hdr.usec_per_frame; + //fport[i]->usec_per_frame = avi_hdr.avih_hdr.usec_per_frame; pjmedia_format_init_video(&fport[i]->base.info.fmt, fport[i]->fmt_id, strf_hdr->biWidth, @@ -540,7 +570,7 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool_, &avi_hdr.strf_hdr[fport[i]->stream_id].strf_audio_hdr; fport[i]->bits_per_sample = strf_hdr->bits_per_sample; - fport[i]->usec_per_frame = avi_hdr.avih_hdr.usec_per_frame; + //fport[i]->usec_per_frame = avi_hdr.avih_hdr.usec_per_frame; pjmedia_format_init_audio(&fport[i]->base.info.fmt, fport[i]->fmt_id, strf_hdr->sample_rate, @@ -559,17 +589,61 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool_, } } - pj_strdup2(pool, &fport[i]->base.info.name, filename); + pj_ansi_snprintf(port_name, sizeof(port_name), "%s-of-%s", + pjmedia_type_name(fport[i]->base.info.fmt.type), + get_fname(filename)); + pj_strdup2(pool, &fport[i]->base.info.name, port_name); } /* Done. */ - *p_streams = pj_pool_alloc(pool, sizeof(pjmedia_avi_streams)); + *p_streams = pj_pool_calloc(pool, 1, sizeof(pjmedia_avi_streams)); (*p_streams)->num_streams = nstr; (*p_streams)->streams = pj_pool_calloc(pool, (*p_streams)->num_streams, sizeof(pjmedia_port *)); for (i = 0; i < nstr; i++) (*p_streams)->streams[i] = &fport[i]->base; + /* Create AV synchronizer, if not disabled */ + if ((options & PJMEDIA_AVI_FILE_NO_SYNC) == 0) { + pjmedia_av_sync *avsync; + pj_timestamp ts_zero = {{0}}; + pjmedia_av_sync_setting setting; + + pjmedia_av_sync_setting_default(&setting); + setting.name = (char*) get_fname(filename); + status = pjmedia_av_sync_create(pool, &setting, &avsync); + if (status != PJ_SUCCESS) + goto on_error; + + (*p_streams)->avsync = avsync; + + for (i = 0; i < nstr; i++) { + pjmedia_av_sync_media_setting med_setting; + + pjmedia_av_sync_media_setting_default(&med_setting); + med_setting.type = fport[i]->base.info.fmt.type; + med_setting.name = (char*)pjmedia_type_name(med_setting.type); + if (med_setting.type == PJMEDIA_TYPE_AUDIO) { + med_setting.clock_rate = PJMEDIA_PIA_SRATE(&fport[i]->base.info); + } else if (med_setting.type == PJMEDIA_TYPE_VIDEO) { + med_setting.clock_rate = VIDEO_CLOCK_RATE; + } + status = pjmedia_av_sync_add_media(avsync, &med_setting, + &fport[i]->avsync_media); + if (status != PJ_SUCCESS) + goto on_error; + + /* Set reference timestamps to zeroes */ + status = pjmedia_av_sync_update_ref(fport[i]->avsync_media, + &ts_zero, &ts_zero); + if (status != PJ_SUCCESS) + goto on_error; + + /* Set pointer to AVI streams */ + fport[i]->avi_streams = *p_streams; + } + } + status = pj_grp_lock_add_handler(grp_lock, NULL, *p_streams, &streams_on_destroy); if (status != PJ_SUCCESS) @@ -592,6 +666,15 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool_, for (i = 1; i < nstr; i++) pjmedia_port_destroy(&fport[i]->base); } + + if (*p_streams && (*p_streams)->avsync) { + for (i = 0; i < nstr; i++) { + if (fport[i]->avsync_media) + pjmedia_av_sync_del_media(NULL, fport[i]->avsync_media); + } + pjmedia_av_sync_destroy((*p_streams)->avsync); + } + pj_pool_release(pool); if (status == AVI_EOF) @@ -721,6 +804,108 @@ static pj_status_t file_on_event(pjmedia_event *event, } +static pj_status_t skip_forward(pjmedia_port *this_port, pj_size_t frames) +{ + struct avi_reader_port *fport = (struct avi_reader_port*)this_port; + pj_status_t status = PJ_SUCCESS; + pj_ssize_t remainder = frames; + pjmedia_type type = fport->base.info.fmt.type; + pj_bool_t is_pcm = PJ_FALSE; + + /* For audio, skip current chunk first */ + if (type == PJMEDIA_TYPE_AUDIO) { + is_pcm = (fport->fmt_id!=PJMEDIA_FORMAT_PCMA && + fport->fmt_id!=PJMEDIA_FORMAT_PCMU); + if (fport->size_left > 0) { + pj_ssize_t seek_size = is_pcm? frames * 2 : frames; + seek_size = PJ_MIN(seek_size, fport->size_left); + status = pj_file_setpos(fport->fd, seek_size, PJ_SEEK_CUR); + if (status != PJ_SUCCESS) + return status; + + fport->size_left -= seek_size; + remainder -= (seek_size / (is_pcm? 2 : 1)); + + fport->frame_cnt += (seek_size / (is_pcm? 2 : 1)); + fport->next_ts.u64 = fport->frame_cnt; + } + } + + while (remainder) { + pjmedia_avi_subchunk ch = {0, 0}; + unsigned stream_id; + char *cid; + + /* Need to skip new chunk */ + pj_assert(fport->size_left == 0); + + /* Data is padded to the nearest WORD boundary */ + if (fport->pad) { + status = pj_file_setpos(fport->fd, fport->pad, PJ_SEEK_CUR); + fport->pad = 0; + } + + status = file_read(fport->fd, &ch, sizeof(pjmedia_avi_subchunk)); + if (status != PJ_SUCCESS) + return status; + + PJ_CHECK_OVERFLOW_UINT32_TO_LONG(ch.len, return PJ_EINVAL); + fport->pad = (pj_uint8_t)ch.len & 1; + + cid = (char *)&ch.id; + if (pj_isdigit(cid[0]) && pj_isdigit(cid[1])) + stream_id = (cid[0] - '0') * 10 + (cid[1] - '0'); + else + stream_id = 1000; + + /* We are only interested in data with our stream id */ + if (stream_id != fport->stream_id) { + if (COMPARE_TAG(ch.id, PJMEDIA_AVI_LIST_TAG)) + PJ_LOG(5, (THIS_FILE, "Unsupported LIST tag found in " + "the movi data.")); + else if (COMPARE_TAG(ch.id, PJMEDIA_AVI_RIFF_TAG)) { + PJ_LOG(3, (THIS_FILE, "Unsupported format: multiple " + "AVIs in a single file.")); + return PJ_ENOTSUP; + } + + status = pj_file_setpos(fport->fd, ch.len, PJ_SEEK_CUR); + continue; + } + + /* Found new chunk */ + fport->size_left = ch.len; + if (type == PJMEDIA_TYPE_AUDIO) { + pj_ssize_t seek_size = remainder * (is_pcm? 2 : 1); + seek_size = PJ_MIN(seek_size, fport->size_left); + status = pj_file_setpos(fport->fd, seek_size, PJ_SEEK_CUR); + if (status != PJ_SUCCESS) + return status; + + fport->size_left -= seek_size; + remainder -= (seek_size / (is_pcm? 2 : 1)); + + fport->frame_cnt += (seek_size / (is_pcm? 2 : 1)); + fport->next_ts.u64 = fport->frame_cnt; + } else { + status = pj_file_setpos(fport->fd, fport->size_left, PJ_SEEK_CUR); + if (status != PJ_SUCCESS) + return status; + fport->size_left = 0; + remainder -= 1; + + fport->frame_cnt++; + fport->next_ts.u64 = ((pj_uint64_t)fport->frame_cnt * + VIDEO_CLOCK_RATE * + fport->base.info.fmt.det.vid.fps.denum/ + fport->base.info.fmt.det.vid.fps.num); + } + } /* while (remainder) */ + + return PJ_SUCCESS; +} + + /* * Get frame from file. */ @@ -730,73 +915,162 @@ static pj_status_t avi_get_frame(pjmedia_port *this_port, struct avi_reader_port *fport = (struct avi_reader_port*)this_port; pj_status_t status = PJ_SUCCESS; pj_ssize_t size_read = 0, size_to_read = 0; + pjmedia_port_info* port_info = &fport->base.info; pj_assert(fport->base.info.signature == SIGNATURE); + /* Synchronize media */ + if (fport->avsync_media && !fport->eof) { + pj_int32_t adjust_delay; + + /* Just return if we are increasing delay */ + if (fport->slow_down_frm) { + fport->slow_down_frm--; + frame->type = PJMEDIA_FRAME_TYPE_NONE; + frame->size = 0; + return PJ_SUCCESS; + } + + status = pjmedia_av_sync_update_pts(fport->avsync_media, + &fport->next_ts, &adjust_delay); + if (status == PJ_SUCCESS && adjust_delay) { + pj_ssize_t frames = 0; + + /* If speed up is requested for more than 1 seconds, + * the stream may just be resumed, fast forward. + */ + if (adjust_delay < -1000) { + PJ_LOG(4,(THIS_FILE, "%.*s: %s need to fast forward by %dms", + (int)fport->base.info.name.slen, + fport->base.info.name.ptr, + pjmedia_type_name(port_info->fmt.type), + -adjust_delay)); + + if (fport->base.info.fmt.type == PJMEDIA_TYPE_AUDIO) { + frames = -adjust_delay * + port_info->fmt.det.aud.clock_rate / + 1000; + } else { + frames = -adjust_delay * + port_info->fmt.det.vid.fps.num / + port_info->fmt.det.vid.fps.denum / 1000; + } + status = skip_forward(this_port, frames); + if (status != PJ_SUCCESS) + goto on_error2; + } + + /* Otherwise it is a small adjustment, apply for video stream only + * so the audio playback remains smooth. + */ + else if (port_info->fmt.type == PJMEDIA_TYPE_VIDEO) { + pj_bool_t slowdown = adjust_delay > 0; + + adjust_delay = PJ_ABS(adjust_delay); + frames = adjust_delay * + port_info->fmt.det.vid.fps.num / + port_info->fmt.det.vid.fps.denum / 1000; + + if (slowdown) { + PJ_LOG(4, (THIS_FILE, + "%.*s: video need to slow down by %dms", + (int)port_info->name.slen, + port_info->name.ptr, + adjust_delay)); + frame->type = PJMEDIA_FRAME_TYPE_NONE; + frame->size = 0; + + /* Increase delay */ + fport->slow_down_frm = frames; + return PJ_SUCCESS; + } + + PJ_LOG(4,(THIS_FILE, "%.*s: video need to speed up by %dms", + (int)port_info->name.slen, + port_info->name.ptr, + -adjust_delay)); + status = skip_forward(this_port, (frames? frames : 1)); + if (status != PJ_SUCCESS) + goto on_error2; + } + } + } + + /* Set the frame timestamp */ + frame->timestamp.u64 = fport->next_ts.u64; + /* We encountered end of file */ if (fport->eof) { - PJ_LOG(5,(THIS_FILE, "File port %.*s EOF", - (int)fport->base.info.name.slen, - fport->base.info.name.ptr)); + pj_bool_t no_loop = (fport->options & PJMEDIA_AVI_FILE_NO_LOOP); + pj_bool_t rewind_now = PJ_TRUE; - /* Call callback, if any */ - if (fport->cb2) { - pj_bool_t no_loop = (fport->options & PJMEDIA_AVI_FILE_NO_LOOP); - - if (!fport->subscribed) { - status = pjmedia_event_subscribe(NULL, &file_on_event, - fport, fport); - fport->subscribed = (status == PJ_SUCCESS)? PJ_TRUE: - PJ_FALSE; + /* If synchronized, wait all streams to EOF before rewinding */ + if (fport->avsync_media) { + pjmedia_avi_streams *avi_streams = fport->avi_streams; + + rewind_now = (avi_streams->eof_cnt % avi_streams->num_streams)==0; + if (rewind_now) { + pj_timestamp ts_zero = {{0}}; + pjmedia_av_sync_update_ref(fport->avsync_media, + &ts_zero, &ts_zero); } + } - if (fport->subscribed && fport->eof != 2) { - pjmedia_event event; + /* Call callback, if any */ + if (fport->eof != 2) { - if (no_loop) { - /* To prevent the callback from being called repeatedly */ - fport->eof = 2; - } else { - fport->eof = PJ_FALSE; - pj_file_setpos(fport->fd, fport->start_data, PJ_SEEK_SET); + /* To prevent the callback from being called repeatedly */ + fport->eof = 2; + + if (fport->cb2) { + if (!fport->subscribed) { + status = pjmedia_event_subscribe(NULL, &file_on_event, + fport, fport); + fport->subscribed = (status == PJ_SUCCESS)? PJ_TRUE: + PJ_FALSE; } - pjmedia_event_init(&event, PJMEDIA_EVENT_CALLBACK, - NULL, fport); - pjmedia_event_publish(NULL, fport, &event, - PJMEDIA_EVENT_PUBLISH_POST_EVENT); - } - - /* Should not access player port after this since - * it might have been destroyed by the callback. - */ - frame->type = PJMEDIA_FRAME_TYPE_NONE; - frame->size = 0; - - return (no_loop? PJ_EEOF: PJ_SUCCESS); + if (fport->subscribed) { + pjmedia_event event; - } else if (fport->cb) { - status = (*fport->cb)(this_port, fport->base.port_data.pdata); + pjmedia_event_init(&event, PJMEDIA_EVENT_CALLBACK, + NULL, fport); + pjmedia_event_publish(NULL, fport, &event, + PJMEDIA_EVENT_PUBLISH_POST_EVENT); + } + + /* Should not access player port after this since + * it might have been destroyed by the callback. + */ + frame->type = PJMEDIA_FRAME_TYPE_NONE; + frame->size = 0; + status = PJ_SUCCESS; + + } else if (fport->cb) { + status = (*fport->cb)(this_port, fport->base.port_data.pdata); + } } /* If callback returns non PJ_SUCCESS or 'no loop' is specified, * return immediately (and don't try to access player port since * it might have been destroyed by the callback). */ - if ((status != PJ_SUCCESS) || - (fport->options & PJMEDIA_AVI_FILE_NO_LOOP)) - { + if (status != PJ_SUCCESS || no_loop || !rewind_now) { frame->type = PJMEDIA_FRAME_TYPE_NONE; frame->size = 0; - return PJ_EEOF; + return (no_loop? PJ_EEOF : status); } - /* Rewind file */ - PJ_LOG(5,(THIS_FILE, "File port %.*s rewinding..", - (int)fport->base.info.name.slen, - fport->base.info.name.ptr)); - fport->eof = PJ_FALSE; - pj_file_setpos(fport->fd, fport->start_data, PJ_SEEK_SET); + if (rewind_now) { + PJ_LOG(5,(THIS_FILE, "AVI player port %.*s rewinding..", + (int)fport->base.info.name.slen, + fport->base.info.name.ptr)); + + pj_file_setpos(fport->fd, fport->start_data, PJ_SEEK_SET); + fport->eof = PJ_FALSE; + fport->frame_cnt = 0; + fport->next_ts.u64 = 0; + } } /* For PCMU/A audio stream, reduce frame size to half (temporarily). */ @@ -903,7 +1177,7 @@ static pj_status_t avi_get_frame(pjmedia_port *this_port, break; } while(1); - frame->timestamp.u64 = fport->next_ts.u64; + if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) { /* Decode PCMU/A frame */ @@ -931,37 +1205,44 @@ static pj_status_t avi_get_frame(pjmedia_port *this_port, frame->size <<= 1; } - if (fport->usec_per_frame) { - fport->next_ts.u64 += (fport->usec_per_frame * - fport->base.info.fmt.det.aud.clock_rate / - 1000000); - } else { - fport->next_ts.u64 += (frame->size * - fport->base.info.fmt.det.aud.clock_rate / - (fport->base.info.fmt.det.aud.avg_bps / 8)); - } + fport->frame_cnt += (frame->size >> 1); + fport->next_ts.u64 = fport->frame_cnt; } else { - if (fport->usec_per_frame) { - fport->next_ts.u64 += (fport->usec_per_frame * VIDEO_CLOCK_RATE / - 1000000); - } else { - fport->next_ts.u64 += (frame->size * VIDEO_CLOCK_RATE / - (fport->base.info.fmt.det.vid.avg_bps / 8)); - } + fport->frame_cnt++; + fport->next_ts.u64 = ((pj_uint64_t)fport->frame_cnt * + VIDEO_CLOCK_RATE * + fport->base.info.fmt.det.vid.fps.denum/ + fport->base.info.fmt.det.vid.fps.num); } return PJ_SUCCESS; on_error2: - if (status == AVI_EOF) { + if (status == AVI_EOF && !fport->eof) { + + /* Reset AV sync on the last stream encountering EOF */ + if (fport->avsync_media) { + pjmedia_avi_streams* avi_streams = fport->avi_streams; + + if (avi_streams->avsync && + (++avi_streams->eof_cnt % avi_streams->num_streams == 0)) + { + pjmedia_av_sync_reset(avi_streams->avsync); + } + } + fport->eof = PJ_TRUE; + PJ_LOG(5,(THIS_FILE, "AVI player port %.*s EOF", + (int)fport->base.info.name.slen, + fport->base.info.name.ptr)); + size_to_read -= size_read; if (size_to_read == (pj_ssize_t)frame->size) { /* Frame is empty */ frame->type = PJMEDIA_FRAME_TYPE_NONE; frame->size = 0; - return PJ_EEOF; + return PJ_EEOF; } pj_bzero((char *)frame->buf + frame->size - size_to_read, size_to_read); @@ -969,7 +1250,9 @@ static pj_status_t avi_get_frame(pjmedia_port *this_port, return PJ_SUCCESS; } - return status; + frame->type = PJMEDIA_FRAME_TYPE_NONE; + frame->size = 0; + return (status==AVI_EOF? PJ_EEOF : status); } /* @@ -988,6 +1271,12 @@ static pj_status_t avi_on_destroy(pjmedia_port *this_port) if (fport->fd != (pj_oshandle_t) (pj_ssize_t)-1) pj_file_close(fport->fd); + + if (fport->avsync_media) { + pjmedia_av_sync_del_media(NULL, fport->avsync_media); + fport->avsync_media = NULL; + } + return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c index 9fafa6c71b..59070e799a 100644 --- a/pjmedia/src/pjmedia/jbuf.c +++ b/pjmedia/src/pjmedia/jbuf.c @@ -110,6 +110,7 @@ struct pjmedia_jbuf calculation */ int jb_min_shrink_gap; /**< How often can we shrink */ discard_algo jb_discard_algo; /**< Discard algorithm */ + unsigned jb_min_delay; /**< Minimum delay, in frames */ /* Buffer */ jb_framelist_t jb_framelist; /**< the buffer */ @@ -857,9 +858,16 @@ static void jbuf_discard_static(pjmedia_jbuf *jb) * so just disable it when progressive discard is active. */ int diff, burst_level; + unsigned cur_size; burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level); - diff = jb_framelist_eff_size(&jb->jb_framelist) - burst_level*2; + cur_size = jb_framelist_eff_size(&jb->jb_framelist); + + /* Don't discard if delay is lower than or equal to setting */ + if (cur_size <= jb->jb_min_delay) + return; + + diff = cur_size - burst_level*2; if (diff >= STA_DISC_SAFE_SHRINKING_DIFF) { int seq_origin; @@ -898,10 +906,10 @@ static void jbuf_discard_progressive(pjmedia_jbuf *jb) if (jb->jb_last_op != JB_OP_PUT) return; - /* Check if latency is longer than burst */ + /* Check if latency is longer than burst or minimum delay */ cur_size = jb_framelist_eff_size(&jb->jb_framelist); burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level); - if (cur_size <= burst_level) { + if (cur_size <= burst_level || cur_size <= jb->jb_min_delay) { /* Reset any scheduled discard */ jb->jb_discard_dist = 0; return; @@ -919,7 +927,7 @@ static void jbuf_discard_progressive(pjmedia_jbuf *jb) (PJMEDIA_JBUF_PRO_DISC_MAX_BURST-PJMEDIA_JBUF_PRO_DISC_MIN_BURST); /* Calculate current discard distance */ - overflow = cur_size - burst_level; + overflow = cur_size - PJ_MAX(burst_level, jb->jb_min_delay); discard_dist = T * jb->jb_frame_ptime_denum / overflow / jb->jb_frame_ptime; @@ -1146,6 +1154,10 @@ PJ_DEF(void) pjmedia_jbuf_get_frame3(pjmedia_jbuf *jb, jb->jb_empty++; + } else if (jb_framelist_eff_size(&jb->jb_framelist) < jb->jb_min_delay) { + *p_frame_type = PJMEDIA_JB_MISSING_FRAME; + if (size) + *size = 0; } else { pjmedia_jb_frame_type ftype = PJMEDIA_JB_NORMAL_FRAME; @@ -1205,6 +1217,7 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_get_state( const pjmedia_jbuf *jb, state->min_prefetch = jb->jb_min_prefetch; state->max_prefetch = jb->jb_max_prefetch; state->max_count = (unsigned)jb->jb_max_count; + state->min_delay_set = jb->jb_min_delay; state->burst = jb->jb_eff_level; state->prefetch = jb->jb_prefetch; @@ -1270,3 +1283,22 @@ PJ_DEF(unsigned) pjmedia_jbuf_remove_frame(pjmedia_jbuf *jb, return count; } + + +PJ_DEF(pj_status_t) pjmedia_jbuf_set_min_delay(pjmedia_jbuf *jb, + unsigned min_delay) +{ + PJ_ASSERT_RETURN(jb, PJ_EINVAL); + + /* Convert milliseconds to frames */ + min_delay *= jb->jb_frame_ptime_denum; + jb->jb_min_delay = min_delay / jb->jb_frame_ptime; + if (min_delay % jb->jb_frame_ptime) + jb->jb_min_delay++; + + /* Should not be higher than half of jitter buffer capacity */ + if (jb->jb_min_delay > jb->jb_max_count/2) + jb->jb_min_delay = (unsigned)jb->jb_max_count/2; + + return PJ_SUCCESS; +} diff --git a/pjmedia/src/pjmedia/rtcp.c b/pjmedia/src/pjmedia/rtcp.c index af080a4e7b..2daa0e3f80 100644 --- a/pjmedia/src/pjmedia/rtcp.c +++ b/pjmedia/src/pjmedia/rtcp.c @@ -574,6 +574,11 @@ static void parse_rtcp_report( pjmedia_rtcp_session *sess, sess->rx_lsr = ((pj_ntohl(sr->ntp_sec) & 0x0000FFFF) << 16) | ((pj_ntohl(sr->ntp_frac) >> 16) & 0xFFFF); + /* Save RTP & NTP timestamps of RTCP packet */ + sess->rx_lsr_ts = pj_ntohl(sr->rtp_ts); + sess->rx_lsr_ntp.u32.hi = pj_ntohl(sr->ntp_sec); + sess->rx_lsr_ntp.u32.lo = pj_ntohl(sr->ntp_frac); + /* Calculate SR arrival time for DLSR */ pj_get_timestamp(&sess->rx_lsr_time); @@ -943,7 +948,7 @@ PJ_DEF(void) pjmedia_rtcp_build_rtcp(pjmedia_rtcp_session *sess, * sent RTCP SR. */ if (sess->stat.tx.pkt != pj_ntohl(sess->rtcp_sr_pkt.sr.sender_pcount)) { - pj_time_val ts_time; + //pj_time_val ts_time; pj_uint32_t rtp_ts; /* So we should send RTCP SR */ @@ -963,11 +968,13 @@ PJ_DEF(void) pjmedia_rtcp_build_rtcp(pjmedia_rtcp_session *sess, sr->ntp_frac = pj_htonl(ntp.lo); /* Fill in RTP timestamp (corresponds to NTP timestamp) in SR. */ - ts_time.sec = ntp.hi - sess->tv_base.sec - JAN_1970; - ts_time.msec = (long)(ntp.lo * 1000.0 / 0xFFFFFFFF); - rtp_ts = sess->rtp_ts_base + - (pj_uint32_t)(sess->clock_rate*ts_time.sec) + - (pj_uint32_t)(sess->clock_rate*ts_time.msec/1000); + // Use real last transmitted RTP timestamp instead of calculated one. + //ts_time.sec = ntp.hi - sess->tv_base.sec - JAN_1970; + //ts_time.msec = (long)(ntp.lo * 1000.0 / 0xFFFFFFFF); + //rtp_ts = sess->rtp_ts_base + + // (pj_uint32_t)(sess->clock_rate*ts_time.sec) + + // (pj_uint32_t)(sess->clock_rate*ts_time.msec/1000); + rtp_ts = sess->stat.rtp_tx_last_ts; sr->rtp_ts = pj_htonl(rtp_ts); TRACE_((sess->name, "TX RTCP SR: ntp_ts=%p", diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index 1193c5dde3..74a89a6c52 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -213,6 +213,7 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) pjmedia_channel *channel = c_strm->dec; unsigned samples_count, samples_per_frame, samples_required; pj_int16_t *p_out_samp; + pj_uint32_t rtp_ts = 0; pj_status_t status; @@ -270,8 +271,8 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) } /* Get frame from jitter buffer. */ - pjmedia_jbuf_get_frame2(c_strm->jb, channel->buf, &frame_size, - &frame_type, &bit_info); + pjmedia_jbuf_get_frame3(c_strm->jb, channel->buf, &frame_size, + &frame_type, &bit_info, &rtp_ts, NULL); #if TRACE_JB trace_jb_get(c_strm, frame_type, frame_size); @@ -489,6 +490,47 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) } if (!use_dec_buf) samples_count += samples_per_frame; + + /* Update synchronizer with presentation time and check if the + * synchronizer requests for delay adjustment. + */ + if (c_strm->av_sync_media) { + pj_timestamp pts = { 0 }; + pj_int32_t delay_req_ms; + + pts.u32.lo = rtp_ts; + status = pjmedia_av_sync_update_pts(c_strm->av_sync_media, + &pts, &delay_req_ms); + if (status == PJ_SUCCESS && delay_req_ms) { + /* Delay adjustment is requested */ + pjmedia_jb_state jb_state; + int target_delay_ms, cur_delay_ms; + + /* Apply delay request to jitter buffer */ + pjmedia_jbuf_get_state(c_strm->jb, &jb_state); + cur_delay_ms = jb_state.min_delay_set * stream->dec_ptime/ + stream->dec_ptime_denum; + target_delay_ms = cur_delay_ms + delay_req_ms; + if (target_delay_ms < 0) + target_delay_ms = 0; + + /* Just for safety (never see in tests), target delay + * should not exceed 5 seconds. + */ + if (target_delay_ms > 5000) { + PJ_LOG(5,(c_strm->port.info.name.ptr, + "Ignored avsync request for excessive delay" + " (current=%dms, target=%dms)!", + cur_delay_ms, target_delay_ms)); + } else if (cur_delay_ms != target_delay_ms) { + pjmedia_jbuf_set_min_delay(c_strm->jb, + target_delay_ms); + PJ_LOG(5,(c_strm->port.info.name.ptr, + "Adjust audio minimal delay to %dms", + target_delay_ms)); + } + } + } } } @@ -522,6 +564,7 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) pjmedia_channel *channel = c_strm->dec; pjmedia_frame_ext *f = (pjmedia_frame_ext*)frame; unsigned samples_per_frame, samples_required; + pj_uint32_t rtp_ts = 0; pj_status_t status; /* Return no frame if channel is paused */ @@ -553,8 +596,8 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) pj_mutex_lock( c_strm->jb_mutex ); /* Get frame from jitter buffer. */ - pjmedia_jbuf_get_frame2(c_strm->jb, channel->buf, &frame_size, - &frame_type, &bit_info); + pjmedia_jbuf_get_frame3(c_strm->jb, channel->buf, &frame_size, + &frame_type, &bit_info, &rtp_ts, NULL); #if TRACE_JB trace_jb_get(c_strm, frame_type, frame_size); @@ -595,6 +638,40 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) c_strm->jb_last_frm_cnt++; } + /* Update synchronizer with presentation time */ + if (c_strm->av_sync_media) { + pj_timestamp pts = { 0 }; + pj_int32_t delay_req_ms; + + pts.u32.lo = rtp_ts; + status = pjmedia_av_sync_update_pts(c_strm->av_sync_media, + &pts, &delay_req_ms); + if (status == PJ_SUCCESS && delay_req_ms) { + /* Delay adjustment is requested */ + pjmedia_jb_state jb_state; + int target_delay_ms, cur_delay_ms; + + /* Increase delay slowly, but decrease delay quickly */ + if (delay_req_ms > 0) + delay_req_ms = delay_req_ms * 3 / 4; + else + delay_req_ms = delay_req_ms * 4 / 3; + + /* Apply delay request to jitter buffer */ + pjmedia_jbuf_get_state(c_strm->jb, &jb_state); + cur_delay_ms = jb_state.min_delay_set * stream->dec_ptime/ + stream->dec_ptime_denum; + target_delay_ms = cur_delay_ms + delay_req_ms; + if (target_delay_ms < 0) + target_delay_ms = 0; + pjmedia_jbuf_set_min_delay(c_strm->jb, target_delay_ms); + + PJ_LOG(5,(c_strm->port.info.name.ptr, + "Adjust minimal delay to %dms", + target_delay_ms)); + } + } + } else { /* Try to generate frame by invoking PLC (when any) */ @@ -1404,12 +1481,14 @@ static pj_status_t on_stream_rx_rtp(pjmedia_stream_common *c_strm, pj_bool_t *pkt_discarded) { pjmedia_stream *stream = (pjmedia_stream*) c_strm; + pj_timestamp ts; pj_status_t status = PJ_SUCCESS; + /* Get the timestamp from the RTP header */ + ts.u64 = pj_ntohl(hdr->ts); + /* Handle incoming DTMF. */ if (hdr->pt == stream->rx_event_pt) { - pj_timestamp ts; - /* Ignore out-of-order packet as it will be detected as new * digit. Also ignore duplicate packet as it serves no use. */ @@ -1417,9 +1496,6 @@ static pj_status_t on_stream_rx_rtp(pjmedia_stream_common *c_strm, goto on_return; } - /* Get the timestamp of the event */ - ts.u64 = pj_ntohl(hdr->ts); - handle_incoming_dtmf(stream, &ts, payload, payloadlen); goto on_return; } @@ -1438,15 +1514,11 @@ static pj_status_t on_stream_rx_rtp(pjmedia_stream_common *c_strm, * to ask the codec to "parse" the payload into multiple frames. */ enum { MAX = 16 }; - pj_timestamp ts; unsigned i, count = MAX; unsigned ts_span; pjmedia_frame frames[MAX]; pj_bzero(frames, sizeof(frames[0]) * MAX); - /* Get the timestamp of the first sample */ - ts.u64 = pj_ntohl(hdr->ts); - /* Parse the payload. */ status = pjmedia_codec_parse(stream->codec, (void*)payload, payloadlen, &ts, &count, frames); @@ -1587,8 +1659,9 @@ static pj_status_t on_stream_rx_rtp(pjmedia_stream_common *c_strm, pj_bool_t discarded; ext_seq = (unsigned)(frames[i].timestamp.u64 / ts_span); - pjmedia_jbuf_put_frame2(c_strm->jb, frames[i].buf, frames[i].size, - frames[i].bit_info, ext_seq, &discarded); + pjmedia_jbuf_put_frame3(c_strm->jb, frames[i].buf, frames[i].size, + frames[i].bit_info, ext_seq, ts.u32.lo, + &discarded); if (discarded) *pkt_discarded = PJ_TRUE; } diff --git a/pjmedia/src/pjmedia/stream_common.c b/pjmedia/src/pjmedia/stream_common.c index 192edf701b..4bd53d9b01 100644 --- a/pjmedia/src/pjmedia/stream_common.c +++ b/pjmedia/src/pjmedia/stream_common.c @@ -577,6 +577,49 @@ pjmedia_stream_common_get_rtp_session_info(pjmedia_stream_common *c_strm, return PJ_SUCCESS; } + +/* + * Set media presentation synchronizer. + */ +PJ_DEF(pj_status_t) +pjmedia_stream_common_set_avsync(pjmedia_stream_common* stream, + pjmedia_av_sync* av_sync) +{ + pj_status_t status = PJ_SUCCESS; + + PJ_ASSERT_RETURN(stream, PJ_EINVAL); + + /* First, remove existing */ + if (stream->av_sync && stream->av_sync_media) { + status = pjmedia_av_sync_del_media(stream->av_sync, + stream->av_sync_media); + stream->av_sync = NULL; + stream->av_sync_media = NULL; + } + + /* Then set a new or reset */ + if (av_sync) { + pjmedia_av_sync_media_setting setting; + + pjmedia_av_sync_media_setting_default(&setting); + setting.type = stream->si->type; + if (stream->si->type == PJMEDIA_TYPE_AUDIO) { + setting.name = "Audio"; + setting.clock_rate = PJMEDIA_PIA_SRATE(&stream->port.info); + } else if (stream->si->type == PJMEDIA_TYPE_VIDEO) { + setting.name = "Video"; + setting.clock_rate = 90000; + } + + stream->av_sync = av_sync; + status = pjmedia_av_sync_add_media(av_sync, &setting, + &stream->av_sync_media); + } + + return status; +} + + static pj_status_t build_rtcp_fb(pjmedia_stream_common *c_strm, void *buf, pj_size_t *length) { diff --git a/pjmedia/src/pjmedia/stream_imp_common.c b/pjmedia/src/pjmedia/stream_imp_common.c index 6ea63bfeb4..2dbbd37231 100755 --- a/pjmedia/src/pjmedia/stream_imp_common.c +++ b/pjmedia/src/pjmedia/stream_imp_common.c @@ -382,6 +382,14 @@ static void on_rx_rtcp( void *data, } pjmedia_rtcp_rx_rtcp(&c_strm->rtcp, pkt, bytes_read); + + /* Update synchronizer with reference time from RTCP-SR */ + if (c_strm->av_sync_media && c_strm->rtcp.rx_lsr_ts) { + pj_timestamp ntp = {0}, ts = {0}; + ntp = c_strm->rtcp.rx_lsr_ntp; + ts.u32.lo = c_strm->rtcp.rx_lsr_ts; + pjmedia_av_sync_update_ref(c_strm->av_sync_media, &ntp, &ts); + } } /* @@ -655,6 +663,10 @@ static void on_destroy(void *arg) c_strm->jb = NULL; } + /* Destroy media synchronizer */ + if (c_strm->av_sync && c_strm->av_sync_media) + pjmedia_stream_common_set_avsync(c_strm, NULL); + #if TRACE_JB if (TRACE_JB_OPENED(c_strm)) { pj_file_close(c_strm->trace_jb_fd); diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c index b97c4fcd15..e8ac2c3b24 100644 --- a/pjmedia/src/pjmedia/vid_stream.c +++ b/pjmedia/src/pjmedia/vid_stream.c @@ -101,6 +101,9 @@ struct pjmedia_vid_stream pjmedia_ratio dec_max_fps; /**< Max fps of decoding dir. */ pjmedia_frame dec_frame; /**< Current decoded frame. */ unsigned dec_delay_cnt; /**< Decoding delay (in frames).*/ + unsigned dec_add_delay_cnt; + /**< Decoding additional delay + for sync (in frames). */ unsigned dec_max_delay; /**< Decoding max delay (in ts).*/ pjmedia_event fmt_event; /**< Buffered fmt_changed event to avoid deadlock */ @@ -353,6 +356,7 @@ static pj_status_t detach_send_manager(send_stream *ss) } e = next; } + ss->mgr = NULL; pj_grp_lock_release(mgr->grp_lock); /* Decrease ref counter */ @@ -373,6 +377,13 @@ static send_entry* get_send_entry(send_stream *ss) pj_sub_timestamp(&min_sent_ts, &idle_ts); pj_grp_lock_acquire(ss->grp_lock); + + /* Make sure send manager has not been detached */ + if (ss->mgr == NULL) { + pj_grp_lock_release(ss->grp_lock); + return NULL; + } + e = ss->free_list.next; while (e != &ss->free_list) { send_entry *next = e->next; @@ -409,6 +420,12 @@ static void send_rtp(send_stream *ss, send_entry *entry) pj_grp_lock_acquire(ss->grp_lock); + /* Make sure send manager has not been detached */ + if (ss->mgr == NULL) { + pj_grp_lock_release(ss->grp_lock); + return; + } + /* Calculate earliest sending time allowed by rate control */ ss->rc_total += entry->buf_size; send_ts.u64 = ss->rc_total * ss->ts_freq.u64 * 8 / ss->rc_bandwidth; @@ -646,9 +663,11 @@ static pj_status_t on_stream_rx_rtp(pjmedia_stream_common *c_strm, } if (can_decode) { + pj_size_t old_size = stream->dec_frame.size; + stream->dec_frame.size = stream->dec_max_size; if (decode_frame(stream, &stream->dec_frame) != PJ_SUCCESS) { - stream->dec_frame.size = 0; + stream->dec_frame.size = old_size; } } } @@ -1049,7 +1068,9 @@ static pj_status_t decode_frame(pjmedia_vid_stream *stream, frm_pkt_cnt = cnt; /* Is it time to decode? Check with minimum delay setting */ - if (++frm_cnt == stream->dec_delay_cnt) { + if (++frm_cnt == stream->dec_delay_cnt + + stream->dec_add_delay_cnt) + { got_frame = PJ_TRUE; break; } @@ -1299,6 +1320,95 @@ static pj_status_t get_frame(pjmedia_port *port, pj_grp_lock_release( c_strm->grp_lock ); + /* Update synchronizer with presentation time */ + if (c_strm->av_sync_media && frame->type != PJMEDIA_FRAME_TYPE_NONE) { + pj_timestamp pts = { 0 }; + pj_int32_t delay_req_ms; + pj_status_t status; + + pts = frame->timestamp; + status = pjmedia_av_sync_update_pts(c_strm->av_sync_media, &pts, + &delay_req_ms); + if (status == PJ_SUCCESS && delay_req_ms) { + /* Delay adjustment is requested */ + int target_delay_ms, cur_delay_ms, target_delay_cnt; + unsigned last_add_delay_cnt; + + /* Apply delay request */ + last_add_delay_cnt = stream->dec_add_delay_cnt; + cur_delay_ms = (stream->dec_delay_cnt+stream->dec_add_delay_cnt) * + 1000 * + stream->dec_max_fps.denum / + stream->dec_max_fps.num; + target_delay_ms = cur_delay_ms + delay_req_ms; + target_delay_cnt = target_delay_ms * stream->dec_max_fps.num / + stream->dec_max_fps.denum / 1000; + if (target_delay_cnt <= (int)stream->dec_delay_cnt) + stream->dec_add_delay_cnt = 0; + else + stream->dec_add_delay_cnt = target_delay_cnt - + stream->dec_delay_cnt; + + /* Just for safety (never see in tests), target delay should not + * exceed 5 seconds. + */ + if (target_delay_ms > 5000) { + stream->dec_add_delay_cnt = last_add_delay_cnt; + PJ_LOG(5,(c_strm->port.info.name.ptr, + "Ignored avsync request for excessive delay" + " (current=%dms, target=%dms)!", + cur_delay_ms, target_delay_ms)); + } + + if (stream->dec_add_delay_cnt != last_add_delay_cnt) { + PJ_LOG(5,(c_strm->port.info.name.ptr, + "Adjust video minimal delay to (%d+%d) frames", + stream->dec_delay_cnt, stream->dec_add_delay_cnt)); + } + + /* When requested to speed-up, try to skip frames */ + if (delay_req_ms < 0) { + enum { MAX_SKIP_MS = 500 }; + unsigned cnt = 0, max_cnt; + + /* Translate milliseconds to number of frames to drop, + * apply upper limit to avoid blocking too long. + */ + max_cnt = PJ_MIN(-delay_req_ms, MAX_SKIP_MS) * + stream->dec_max_fps.num / + stream->dec_max_fps.denum / 1000; + + /* Round up one frame, the decoded frame will be played later + * in the next get_frame(). + */ + max_cnt++; + + while (cnt < max_cnt) { + pj_size_t old_size; + + pj_grp_lock_acquire( c_strm->grp_lock ); + old_size = stream->dec_frame.size; + stream->dec_frame.size = stream->dec_max_size; + if (decode_frame(stream, &stream->dec_frame)==PJ_SUCCESS) + { + cnt++; + } else { + /* Revert dec_frame to last successful decoding */ + stream->dec_frame.size = old_size; + pj_grp_lock_release( c_strm->grp_lock ); + break; + } + pj_grp_lock_release( c_strm->grp_lock ); + } + + if (cnt) { + PJ_LOG(5,(c_strm->port.info.name.ptr, + "Skipped %d frames to reduce delay", cnt)); + } + } + } + } + return PJ_SUCCESS; } @@ -1868,8 +1978,11 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy( pjmedia_vid_stream *stream ) c_strm->dec->port.get_frame = NULL; /* Detach from sending manager */ - if (stream->send_stream) + if (stream->send_stream) { + pj_grp_lock_acquire(c_strm->grp_lock); detach_send_manager(stream->send_stream); + pj_grp_lock_release(c_strm->grp_lock); + } #if TRACE_RC { diff --git a/pjsip-apps/src/swig/symbols.i b/pjsip-apps/src/swig/symbols.i index 8f26b22be6..ac7456db3a 100644 --- a/pjsip-apps/src/swig/symbols.i +++ b/pjsip-apps/src/swig/symbols.i @@ -451,7 +451,8 @@ typedef enum pjmedia_vid_packing typedef enum pjmedia_vid_stream_rc_method { PJMEDIA_VID_STREAM_RC_NONE = 0, - PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING = 1 + PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING = 1, + PJMEDIA_VID_STREAM_RC_SEND_THREAD = 2 } pjmedia_vid_stream_rc_method; enum pjmedia_file_writer_option @@ -932,7 +933,8 @@ typedef enum pjsua_call_flag PJSUA_CALL_REINIT_MEDIA = 16, PJSUA_CALL_UPDATE_VIA = 32, PJSUA_CALL_UPDATE_TARGET = 64, - PJSUA_CALL_SET_MEDIA_DIR = 128 + PJSUA_CALL_SET_MEDIA_DIR = 128, + PJSUA_CALL_NO_MEDIA_SYNC = 256 } pjsua_call_flag; typedef enum pjsua_create_media_transport_flag diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 0fdf4f8b4f..90d1ee5638 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -5663,7 +5663,12 @@ typedef enum pjsua_call_flag /** * Set media direction as specified in pjsua_call_setting.media_dir. */ - PJSUA_CALL_SET_MEDIA_DIR = 128 + PJSUA_CALL_SET_MEDIA_DIR = 128, + + /** + * Disable inter-media synchronization. + */ + PJSUA_CALL_NO_MEDIA_SYNC = 256 } pjsua_call_flag; diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index d2bb7ed816..e8f1548f28 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -249,6 +249,8 @@ struct pjsua_call pj_str_t hangup_reason; /**< Hangup reason. */ pjsua_msg_data *hangup_msg_data;/**< Hangup message data. */ pj_str_t siprec_metadata;/** siprec metadata in body */ + + pjmedia_av_sync *av_sync; /**< Media stream synchronizer */ }; diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index ff57a1733b..640fde4fe4 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -671,6 +671,15 @@ pj_status_t pjsua_aud_channel_update(pjsua_call_media *call_med, goto on_return; } + /* Add stream to synchronizer */ + if (call->av_sync) { + status = pjmedia_stream_common_set_avsync( + (pjmedia_stream_common*)call_med->strm.a.stream, + call->av_sync); + if (status != PJ_SUCCESS) + goto on_return; + } + /* Start stream */ status = pjmedia_stream_start(call_med->strm.a.stream); if (status != PJ_SUCCESS) { diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index d40665ab40..8df825c4e8 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -3481,8 +3481,14 @@ pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id) if (dlg && pj_log_get_level() >= 3) log_call_dump(call_id); + /* Stop all media */ stop_media_session(call_id); + /* Destroy media synchronizer */ + if (call->av_sync) + pjmedia_av_sync_destroy(call->av_sync); + call->av_sync = NULL; + /* Stop trickle ICE timer */ if (call->trickle_ice.trickling > PJSUA_OP_STATE_NULL) { call->trickle_ice.trickling = PJSUA_OP_STATE_NULL; @@ -4473,6 +4479,34 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, pj_memcpy(call->media, call->media_prov, sizeof(call->media_prov[0]) * call->med_prov_cnt); + /* Create/reset synchronizer */ + if ((call->opt.flag & PJSUA_CALL_NO_MEDIA_SYNC)==0 && + (maudcnt+mvidcnt) > 1) + { + if (call->av_sync) { + pjmedia_av_sync_reset(call->av_sync); + } else { + pjmedia_av_sync_setting setting; + char name[PJ_MAX_OBJ_NAME]; + + pj_ansi_snprintf(name, sizeof(name), "avsync-call_%02d", call_id); + pjmedia_av_sync_setting_default(&setting); + setting.is_streaming = PJ_TRUE; + setting.name = name; + status = pjmedia_av_sync_create(tmp_pool, &setting, &call->av_sync); + if (status != PJ_SUCCESS) { + PJ_PERROR(3, (THIS_FILE, status, + "Call %d: Failed to create synchronizer", call_id)); + } + } + } + + /* Destroy existing synchronizer if synchronization is cancelled */ + else if ((call->opt.flag & PJSUA_CALL_NO_MEDIA_SYNC) && call->av_sync) { + pjmedia_av_sync_destroy(call->av_sync); + call->av_sync = NULL; + } + /* Process each media stream */ for (mi=0; mi < call->med_cnt; ++mi) { pjsua_call_media *call_med = &call->media[mi]; diff --git a/pjsip/src/pjsua-lib/pjsua_vid.c b/pjsip/src/pjsua-lib/pjsua_vid.c index 6204a29b53..24fb0d3e54 100644 --- a/pjsip/src/pjsua-lib/pjsua_vid.c +++ b/pjsip/src/pjsua-lib/pjsua_vid.c @@ -1196,6 +1196,15 @@ pj_status_t pjsua_vid_channel_update(pjsua_call_media *call_med, if (status != PJ_SUCCESS) goto on_error; + /* Add stream to synchronizer */ + if (call->av_sync) { + status = pjmedia_stream_common_set_avsync( + (pjmedia_stream_common*)call_med->strm.v.stream, + call->av_sync); + if (status != PJ_SUCCESS) + goto on_error; + } + /* Subscribe to video stream events */ pjmedia_event_subscribe(NULL, &call_media_on_event, call_med, call_med->strm.v.stream); From 9f3a3d4b2e96e6ec1ba836fdc118cf6392dc5914 Mon Sep 17 00:00:00 2001 From: Amilcar Ubiera Date: Mon, 7 Apr 2025 02:08:25 -0500 Subject: [PATCH 232/491] pool_i.h: Check available size instead of pointer wrap-around to avoid autological-compare. (#4382) --- pjlib/include/pj/pool_i.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pjlib/include/pj/pool_i.h b/pjlib/include/pj/pool_i.h index c019d8634a..6500c1589e 100644 --- a/pjlib/include/pj/pool_i.h +++ b/pjlib/include/pj/pool_i.h @@ -56,9 +56,9 @@ PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_block *block, pj_size_t alignme // size = (size + alignment) & ~(alignment -1); //} ptr = PJ_POOL_ALIGN_PTR(block->cur, alignment); - if (ptr + size <= block->end && - /* here we check pointer overflow */ - block->cur <= ptr && ptr <= ptr + size) { + if (block->cur <= ptr && /* check pointer overflow */ + block->end - ptr >= size) /* check available size */ + { block->cur = ptr + size; return ptr; } From 998f23de6c2bcf4230ef1633002baf9d463f48ad Mon Sep 17 00:00:00 2001 From: sauwming Date: Mon, 7 Apr 2025 15:35:23 +0800 Subject: [PATCH 233/491] Deprecate pkgconfig.py (#4383) --- aconfigure | 2 +- aconfigure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aconfigure b/aconfigure index 723f048e6d..b583855b47 100755 --- a/aconfigure +++ b/aconfigure @@ -5395,7 +5395,7 @@ then : fi -for ac_prog in $host_cpu-$host_os-pkg-config $host-pkg-config pkg-config "python pkgconfig.py" +for ac_prog in $host_cpu-$host_os-pkg-config $host-pkg-config pkg-config do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 diff --git a/aconfigure.ac b/aconfigure.ac index fdecf082bf..9e6752e021 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -115,7 +115,7 @@ AC_CHECK_LIB(winmm,puts) AC_CHECK_LIB(socket,puts) AC_CHECK_LIB(rt,puts) AC_CHECK_LIB(m,sin) -AC_CHECK_PROGS(PKG_CONFIG,$host_cpu-$host_os-pkg-config $host-pkg-config pkg-config "python pkgconfig.py",none) +AC_CHECK_PROGS(PKG_CONFIG,$host_cpu-$host_os-pkg-config $host-pkg-config pkg-config,none) dnl dnl libuuid From 950ff8c1744c659a336c0b767cf3b2e163cf9721 Mon Sep 17 00:00:00 2001 From: Riza Sulistyo Date: Wed, 9 Apr 2025 08:51:01 +0700 Subject: [PATCH 234/491] Android unit test (#4372) * Add android unit test * Add checks * Add missing update gradle version * Use polling to wait for specific view to be displayed/visible --- .../swig/java/android/app-kotlin/build.gradle | 18 +- .../pjsip/pjsua2/app_kotlin/MainActivity.kt | 4 +- .../src/main/res/layout/activity_main.xml | 4 +- .../src/swig/java/android/app/build.gradle | 29 ++- .../app/src/androidTest/AndroidManifest.xml | 14 ++ .../java/org/pjsip/pjsua2/app/Pjsua2Test.java | 233 ++++++++++++++++++ .../org/pjsip/pjsua2/app/MainActivity.java | 96 ++++++-- .../main/java/org/pjsip/pjsua2/app/MyApp.java | 64 ++++- .../app/src/main/res/layout/activity_main.xml | 10 +- .../main/res/layout/dlg_account_config.xml | 124 +++++----- .../app/src/main/res/layout/dlg_add_buddy.xml | 43 ++-- .../android/app/src/main/res/menu/call.xml | 5 +- .../android/app/src/main/res/menu/main.xml | 8 +- pjsip-apps/src/swig/java/android/build.gradle | 2 +- .../src/swig/java/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 4 +- .../src/swig/java/android/pjsua2/build.gradle | 3 +- 17 files changed, 542 insertions(+), 122 deletions(-) create mode 100644 pjsip-apps/src/swig/java/android/app/src/androidTest/AndroidManifest.xml create mode 100644 pjsip-apps/src/swig/java/android/app/src/androidTest/java/org/pjsip/pjsua2/app/Pjsua2Test.java create mode 100644 pjsip-apps/src/swig/java/android/gradle.properties diff --git a/pjsip-apps/src/swig/java/android/app-kotlin/build.gradle b/pjsip-apps/src/swig/java/android/app-kotlin/build.gradle index 6e0ac29ad2..dd094a2425 100644 --- a/pjsip-apps/src/swig/java/android/app-kotlin/build.gradle +++ b/pjsip-apps/src/swig/java/android/app-kotlin/build.gradle @@ -6,6 +6,8 @@ plugins { android { compileSdk 33 + buildFeatures.buildConfig true + defaultConfig { applicationId "org.pjsip.pjsua2.app_kotlin" minSdkVersion 23 @@ -20,6 +22,18 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + flavorDimensions "version" + productFlavors { + demo { + isDefault true + dimension "version" + buildConfigField "int", "SIP_PORT", "6000" + } + ciTest { + dimension "version" + buildConfigField "int", "SIP_PORT", "6677" + } + } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 @@ -33,7 +47,7 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'com.android.support:appcompat-v7:28.0.0' - implementation 'com.android.support.constraint:constraint-layout:2.0.4' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation project(path: ':pjsua2') } \ No newline at end of file diff --git a/pjsip-apps/src/swig/java/android/app-kotlin/src/main/java/org/pjsip/pjsua2/app_kotlin/MainActivity.kt b/pjsip-apps/src/swig/java/android/app-kotlin/src/main/java/org/pjsip/pjsua2/app_kotlin/MainActivity.kt index a48e485c6b..ceab654976 100644 --- a/pjsip-apps/src/swig/java/android/app-kotlin/src/main/java/org/pjsip/pjsua2/app_kotlin/MainActivity.kt +++ b/pjsip-apps/src/swig/java/android/app-kotlin/src/main/java/org/pjsip/pjsua2/app_kotlin/MainActivity.kt @@ -5,8 +5,8 @@ import android.hardware.camera2.CameraManager import android.os.Bundle import android.os.Handler import android.os.Message -import android.support.v4.app.ActivityCompat -import android.support.v7.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.appcompat.app.AppCompatActivity; import android.view.SurfaceHolder import android.view.SurfaceView import android.view.View diff --git a/pjsip-apps/src/swig/java/android/app-kotlin/src/main/res/layout/activity_main.xml b/pjsip-apps/src/swig/java/android/app-kotlin/src/main/res/layout/activity_main.xml index c461d74827..491c08c46a 100644 --- a/pjsip-apps/src/swig/java/android/app-kotlin/src/main/res/layout/activity_main.xml +++ b/pjsip-apps/src/swig/java/android/app-kotlin/src/main/res/layout/activity_main.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/pjsip-apps/src/swig/java/android/app/build.gradle b/pjsip-apps/src/swig/java/android/app/build.gradle index 9189069d65..61ed9933f3 100644 --- a/pjsip-apps/src/swig/java/android/app/build.gradle +++ b/pjsip-apps/src/swig/java/android/app/build.gradle @@ -2,11 +2,14 @@ apply plugin: 'com.android.application' android { compileSdk 33 + buildFeatures.buildConfig true + defaultConfig { applicationId "org.pjsip.pjsua2.app" minSdkVersion 23 targetSdkVersion 33 + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { @@ -15,10 +18,34 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } } + flavorDimensions "version" + productFlavors { + demo { + isDefault true + dimension "version" + buildConfigField "int", "SIP_PORT", "6000" + buildConfigField "boolean", "IS_TEST", "false" + buildConfigField "String", "TEST_TAG", "\"demo\"" + } + ciTest { + dimension "version" + buildConfigField "int", "SIP_PORT", "6677" + buildConfigField "boolean", "IS_TEST", "true" + buildConfigField "String", "TEST_TAG", "\"ciTest\"" + } + } namespace 'org.pjsip.pjsua2.app' } dependencies { implementation project(path: ':pjsua2') - implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'androidx.appcompat:appcompat:1.6.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + androidTestImplementation 'androidx.test.espresso:espresso-idling-resource:3.5.1' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.5' + androidTestImplementation 'androidx.test:runner:1.5.2' + androidTestImplementation 'androidx.test:rules:1.5.0' + androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' + testImplementation 'junit:junit:4.12' } diff --git a/pjsip-apps/src/swig/java/android/app/src/androidTest/AndroidManifest.xml b/pjsip-apps/src/swig/java/android/app/src/androidTest/AndroidManifest.xml new file mode 100644 index 0000000000..1aa870a269 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/app/src/androidTest/AndroidManifest.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/pjsip-apps/src/swig/java/android/app/src/androidTest/java/org/pjsip/pjsua2/app/Pjsua2Test.java b/pjsip-apps/src/swig/java/android/app/src/androidTest/java/org/pjsip/pjsua2/app/Pjsua2Test.java new file mode 100644 index 0000000000..cce0bb3fc2 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/app/src/androidTest/java/org/pjsip/pjsua2/app/Pjsua2Test.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2025 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.pjsip.pjsua2.app; + +import org.junit.Assert; +import org.pjsip.pjsua2.*; +import org.pjsip.pjsua2.app.BuildConfig; + +import java.io.File; +import java.util.HashMap; + +import android.Manifest; +import android.content.Context; +import android.os.Environment; +import android.util.Log; +import android.util.SparseBooleanArray; +import android.widget.ListView; + +import androidx.test.espresso.IdlingRegistry; +import androidx.test.espresso.IdlingResource; +import androidx.test.espresso.NoMatchingViewException; +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.uiautomator.UiDevice; + +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.replaceText; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.Espresso.onData; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.Visibility; +import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; +import static org.hamcrest.Matchers.anything; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.junit.Assert.*; + +class Holder { + T value; +} + +@RunWith(AndroidJUnit4.class) +public class Pjsua2Test { + final static String TAG = "PJSUA2Test"; + + private UiDevice device; + + @Rule + public ActivityScenarioRule activityScenarioRule = + new ActivityScenarioRule<>(MainActivity.class); + + @Before + public void setup() { + device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + + InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission( + InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName(), + "android.permission.CAMERA" + ); + InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission( + InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName(), + "android.permission.RECORD_AUDIO" + ); + + Context appContext = + InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals(BuildConfig.APPLICATION_ID, appContext.getPackageName()); + } + + private static void checkIsDisplayed(final int viewId, final String viewName) { + try { + onView(withId(viewId)).check(matches(isDisplayed())); + } catch (NoMatchingViewException e) { + Assert.fail("View with ID R.id." + viewName + " was not found"); + } + } + + public static void waitForView(final int viewId, + final long timeoutInMillis, + final boolean isDisplayed) throws Exception + { + long startTime = System.currentTimeMillis(); + long endTime = startTime + timeoutInMillis; + + while (System.currentTimeMillis() < endTime) { + try { + if (isDisplayed) { + onView(withId(viewId)).check(matches(isDisplayed())); + } else { + onView(withId(viewId)).check(matches( + withEffectiveVisibility(Visibility.VISIBLE))); + } + return; + } catch (NoMatchingViewException | AssertionError e) { + Thread.sleep(100); + } + } + + throw new Exception("View with ID " + viewId + " not visible within " + + timeoutInMillis + " milliseconds"); + } + private void takeScreenshot(final String fileName) throws Exception { + UiDevice device = UiDevice.getInstance( + InstrumentationRegistry.getInstrumentation()); + File path = new File(Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_PICTURES).getAbsolutePath(), + "screenshots"); + if (!path.exists()) { + if (!path.mkdirs()) { + System.err.println("Failed to create "+ path.getAbsolutePath() + + " directory"); + return; + } + } + System.out.println("Taking screenshot to : " + path.getAbsolutePath() + + "//" + fileName); + device.takeScreenshot(new File(path, fileName)); + } + + @Test + public void addBuddy() throws Exception{ + String localUri = "sip:localhost"; + + localUri += ":" + Integer.toString(BuildConfig.SIP_PORT); + Log.d(TAG, "Starting addBuddy()"); + + checkIsDisplayed(R.id.buttonAddBuddy, "buttonAddBuddy"); + + activityScenarioRule.getScenario().onActivity(activity -> { + activity.findViewById(R.id.buttonAddBuddy).setTag(BuildConfig.TEST_TAG); + }); + onView(withId(R.id.buttonAddBuddy)).perform(click()); + + Log.d(TAG, "Wait for the dialog to be shown"); + waitForView(R.id.editTextUri, 3000, true); + + Log.d(TAG, "Change the buddy URI"); + onView(withId(R.id.editTextUri)).perform(replaceText(localUri)); + + Log.d(TAG, "Click confirm"); + onView(withText("OK")).perform(click()); + Log.d(TAG, "Done addBuddy()"); + } + @Test + public void callBuddy() throws Exception{ + ListView listView; + final Holder lvHolder = new Holder<>(); + + Log.d(TAG, "Starting callBuddy()"); + + String localUri = "sip:localhost"; + localUri += ":" + Integer.toString(BuildConfig.SIP_PORT); + + checkIsDisplayed(R.id.listViewBuddy, "listViewBuddy"); + + onData(anything()) + .inAdapterView(withId(R.id.listViewBuddy)) + .atPosition(0) + .perform(click()); + + // Retrieve the ListView and the checked item position + activityScenarioRule.getScenario().onActivity(activity -> { + lvHolder.value = activity.findViewById(R.id.listViewBuddy); + }); + listView = lvHolder.value; + assert(listView != null); + listView.setTag(BuildConfig.TEST_TAG); + + int checkedPosition = listView.getCheckedItemPosition(); + SparseBooleanArray checkedItems = listView.getCheckedItemPositions(); + if (checkedItems != null) { + for (int i=0; i checkedBuddy = (HashMap) + listView.getAdapter().getItem(checkedPosition); + + String checkedURI = checkedBuddy.get("uri"); + + Log.d(TAG, "Selected URI is: " + checkedURI); + assertEquals(localUri, checkedURI); + + checkIsDisplayed(R.id.buttonCall, "buttonCall"); + activityScenarioRule.getScenario().onActivity(activity -> { + activity.findViewById(R.id.buttonCall).setTag(BuildConfig.TEST_TAG); + }); + onView(withId(R.id.buttonCall)).perform(click()); + + Log.d(TAG, "Wait for the incoming video to be shown"); + waitForView(R.id.surfaceIncomingVideo, 10000, false); + + onView(withId(R.id.surfaceIncomingVideo)) + .check(matches(withEffectiveVisibility(Visibility.VISIBLE))); + try { + takeScreenshot("make_call_sc.png"); + } catch (Exception e) { + Log.e(TAG, e.getMessage()); + }; + Log.d(TAG, "Done callBuddy()"); + } + +} diff --git a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java index 56ff9623ad..3a5d5d2b72 100644 --- a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java +++ b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java @@ -32,6 +32,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -42,7 +43,7 @@ import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.TextView; -import android.support.v4.app.ActivityCompat; +import androidx.core.app.ActivityCompat; import org.pjsip.PjCameraInfo2; import org.pjsip.pjsua2.AccountConfig; @@ -51,11 +52,17 @@ import org.pjsip.pjsua2.BuddyConfig; import org.pjsip.pjsua2.CallInfo; import org.pjsip.pjsua2.CallOpParam; +import org.pjsip.pjsua2.IntVector; import org.pjsip.pjsua2.OnCallMediaEventParam; import org.pjsip.pjsua2.OnTimerParam; import org.pjsip.pjsua2.StringVector; +import org.pjsip.pjsua2.pjmedia_dir; import org.pjsip.pjsua2.pjsip_inv_state; +import org.pjsip.pjsua2.pjsip_role_e; import org.pjsip.pjsua2.pjsip_status_code; +import org.pjsip.pjsua2.app.R; +import org.pjsip.pjsua2.app.BuildConfig; +import org.pjsip.pjsua2.pjsua_call_flag; import java.util.ArrayList; import java.util.HashMap; @@ -66,6 +73,8 @@ public class MainActivity extends Activity { public static MyApp app = null; public static MyCall currentCall = null; + // for ciTest build only + public static MyCall incomingCall = null; public static MyAccount account = null; public static AccountConfig accCfg = null; public static MyBroadcastReceiver receiver = null; @@ -168,7 +177,8 @@ protected void onCreate(Bundle savedInstanceState) } catch (InterruptedException e) {} } - app.init(this, getFilesDir().getAbsolutePath()); + app.init(this, getFilesDir().getAbsolutePath(), false, + BuildConfig.SIP_PORT, BuildConfig.IS_TEST); } if (app.accList.size() == 0) { @@ -177,6 +187,7 @@ protected void onCreate(Bundle savedInstanceState) accCfg.getNatConfig().setIceEnabled(true); accCfg.getVideoConfig().setAutoTransmitOutgoing(true); accCfg.getVideoConfig().setAutoShowIncoming(true); + accCfg.getVideoConfig().setDefaultCaptureDevice(app.defVidCapDev); account = app.addAcc(accCfg); } else { account = app.accList.get(0); @@ -219,6 +230,15 @@ public void onItemClick(AdapterView parent, } } + @Override + protected void onDestroy() { + super.onDestroy(); + if (receiver != null) { + unregisterReceiver(receiver); + receiver = null; + } + } + @Override public boolean onCreateOptionsMenu(Menu menu) { @@ -232,11 +252,11 @@ public boolean onCreateOptionsMenu(Menu menu) public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.action_acc_config: + case R.id.action_acc_config : dlgAccountSetting(); break; - case R.id.action_quit: + case R.id.action_quit : Message m = Message.obtain(handler, 0); m.sendToTarget(); break; @@ -259,10 +279,19 @@ public boolean handleMessage(Message m) android.os.Process.killProcess(android.os.Process.myPid()); } else if (m.what == MSG_TYPE.CALL_STATE) { - + int callId; + MyCall curCall; CallInfo ci = (CallInfo) m.obj; - - if (currentCall == null || ci == null || ci.getId() != currentCall.getId()) { + if (!BuildConfig.IS_TEST || + ci.getRole() == pjsip_role_e.PJSIP_ROLE_UAC) + { + curCall = currentCall; + } else { + curCall = incomingCall; + } + if ((!BuildConfig.IS_TEST && currentCall == null) || ci == null || + ci.getId() != curCall.getId()) + { System.out.println("Call state event received, but call info is invalid"); return true; } @@ -275,10 +304,14 @@ public boolean handleMessage(Message m) if (ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED) { - currentCall.delete(); - currentCall = null; + if (curCall == currentCall) { + currentCall.delete(); + currentCall = null; + } else if (curCall == incomingCall) { + incomingCall.delete(); + incomingCall = null; + } } - } else if (m.what == MSG_TYPE.CALL_MEDIA_STATE) { /* Forward the message to CallActivity */ @@ -324,8 +357,10 @@ public boolean handleMessage(Message m) final MyCall call = (MyCall) m.obj; CallOpParam prm = new CallOpParam(true); - /* Only one call at anytime */ - if (currentCall != null) { + /* Only one call at anytime for demo mode */ + if ((!BuildConfig.IS_TEST && currentCall != null) || + (BuildConfig.IS_TEST && currentCall == null)) + { prm.setStatusCode(pjsip_status_code.PJSIP_SC_BUSY_HERE); try { call.hangup(prm); @@ -336,13 +371,21 @@ public boolean handleMessage(Message m) } /* Answer with ringing */ - prm.setStatusCode(pjsip_status_code.PJSIP_SC_RINGING); + if (BuildConfig.IS_TEST) { + prm.setStatusCode(pjsip_status_code.PJSIP_SC_OK); + } else { + prm.setStatusCode(pjsip_status_code.PJSIP_SC_RINGING); + } try { call.answer(prm); } catch (Exception e) {} - currentCall = call; - showCallActivity(); + if (BuildConfig.IS_TEST) { + incomingCall = call; + } else { + currentCall = call; + showCallActivity(); + } } else if (m.what == MSG_TYPE.CHANGE_NETWORK) { app.handleNetworkChange(); @@ -448,6 +491,15 @@ public void onClick(DialogInterface dialog,int id) public void makeCall(View view) { + if (BuildConfig.IS_TEST) { + // On ciTest build, don't allow any normal input + if (view.getTag() == null || + !view.getTag().equals(BuildConfig.TEST_TAG)) + { + return; + } + } + if (buddyListSelectedIdx == -1) return; @@ -547,6 +599,14 @@ public void onClick(DialogInterface dialog,int id) { public void addBuddy(View view) { + if (BuildConfig.IS_TEST) { + // On ciTest build, don't allow any normal input + if (view.getTag() == null || + !view.getTag().equals(BuildConfig.TEST_TAG)) + { + return; + } + } dlgAddEditBuddy(null); } @@ -663,6 +723,12 @@ public void notifyChangeNetwork() public void notifyCallMediaEvent(MyCall call, OnCallMediaEventParam prm) { + CallInfo ci; + try { + ci = call.getInfo(); + } catch (Exception e) { + return; + } /* Forward the message to CallActivity */ if (CallActivity.handler_ != null) { Message m = Message.obtain(CallActivity.handler_, diff --git a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java index eb754c1565..5e9f4fcf23 100644 --- a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java +++ b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java @@ -99,6 +99,7 @@ public void onCallMediaState(OnCallMediaStateParam prm) for (int i = 0; i < cmiv.size(); i++) { CallMediaInfo cmi = cmiv.get(i); + if (cmi.getType() == pjmedia_type.PJMEDIA_TYPE_AUDIO && (cmi.getStatus() == pjsua_call_media_status.PJSUA_CALL_MEDIA_ACTIVE || @@ -120,6 +121,10 @@ public void onCallMediaState(OnCallMediaStateParam prm) } else if (cmi.getType() == pjmedia_type.PJMEDIA_TYPE_VIDEO && cmi.getStatus() == pjsua_call_media_status.PJSUA_CALL_MEDIA_ACTIVE) { + if (MyApp.isTest && ci.getRole() == pjsip_role_e.PJSIP_ROLE_UAS) + { + return; + } /* If videoPreview was started, stop it first in case capture device has changed */ if (vidPrevStarted) { try { @@ -154,7 +159,15 @@ public void onCallMediaState(OnCallMediaStateParam prm) @Override public void onCallMediaEvent(OnCallMediaEventParam prm) { - MyApp.observer.notifyCallMediaEvent(this, prm); + CallInfo ci; + try { + ci = getInfo(); + } catch (Exception e) { + return; + } + if (!MyApp.isTest || ci.getRole() == pjsip_role_e.PJSIP_ROLE_UAC) { + MyApp.observer.notifyCallMediaEvent(this, prm); + } } } @@ -327,6 +340,7 @@ public void onTimer(OnTimerParam prm) class MyApp extends pjsua2 { public static MyEndpoint ep = new MyEndpoint(); public static MyAppObserver observer; + public static boolean isTest = false; public ArrayList accList = new ArrayList(); private ArrayList accCfgs = @@ -339,19 +353,32 @@ class MyApp extends pjsua2 { private MyLogWriter logWriter; private final String configName = "pjsua2.json"; - private final int SIP_PORT = 6000; private final int LOG_LEVEL = 4; + private int SIP_PORT = 6000; + int defVidCapDev =pjmedia_vid_dev_std_index.PJMEDIA_VID_DEFAULT_CAPTURE_DEV; public void init(MyAppObserver obs, String app_dir) { - init(obs, app_dir, false); + init(obs, app_dir, false, SIP_PORT, false); } public void init(MyAppObserver obs, String app_dir, boolean own_worker_thread) + { + init(obs, app_dir, own_worker_thread, SIP_PORT, false); + } + + public void init(MyAppObserver obs, String app_dir, + boolean own_worker_thread, int sip_port, boolean is_test) { observer = obs; appDir = app_dir; + isTest = is_test; + + SIP_PORT = sip_port; + + System.out.println("Initializing the library as " + (is_test? + "test":"demo")); /* Create endpoint */ try { @@ -364,7 +391,7 @@ public void init(MyAppObserver obs, String app_dir, /* Load config */ String configPath = appDir + "/" + configName; File f = new File(configPath); - if (f.exists()) { + if (f.exists() && !isTest) { loadConfig(configPath); } else { /* Set 'default' values */ @@ -454,6 +481,27 @@ public void init(MyAppObserver obs, String app_dir, /* Set SIP port back to default for JSON saved config */ sipTpConfig.setPort(SIP_PORT); + if (isTest) { + try { + VideoDevInfoVector2 vidVector = + MyApp.ep.vidDevManager().enumDev2(); + for (int i = 0; i < vidVector.size(); i++) { + VideoDevInfo devInfo = vidVector.get(i); + + if (devInfo.getName().equalsIgnoreCase( + "Colorbar generator")) + { + defVidCapDev = i; + break; + } + } + } catch (Exception e) { + System.out.println(e); + } + } + System.out.println("Use vid index=" + Integer.toString(defVidCapDev) + + " as default capture device"); + /* Create accounts. */ for (int i = 0; i < accCfgs.size(); i++) { MyAccountConfig my_cfg = accCfgs.get(i); @@ -466,7 +514,7 @@ public void init(MyAppObserver obs, String app_dir, /* Enable SRTP optional mode and without requiring SIP TLS transport */ my_cfg.accCfg.getMediaConfig().setSrtpUse(pjmedia_srtp_use.PJMEDIA_SRTP_OPTIONAL); my_cfg.accCfg.getMediaConfig().setSrtpSecureSignaling(0); - + my_cfg.accCfg.getVideoConfig().setDefaultCaptureDevice(defVidCapDev); MyAccount acc = addAcc(my_cfg.accCfg); if (acc == null) continue; @@ -619,8 +667,10 @@ public void handleNetworkChange() public void deinit() { - String configPath = appDir + "/" + configName; - saveConfig(configPath); + if (!isTest) { + String configPath = appDir + "/" + configName; + saveConfig(configPath); + } /* Try force GC to avoid late destroy of PJ objects as they should be * deleted before lib is destroyed. diff --git a/pjsip-apps/src/swig/java/android/app/src/main/res/layout/activity_main.xml b/pjsip-apps/src/swig/java/android/app/src/main/res/layout/activity_main.xml index c63c0210d3..160a4cc4f0 100644 --- a/pjsip-apps/src/swig/java/android/app/src/main/res/layout/activity_main.xml +++ b/pjsip-apps/src/swig/java/android/app/src/main/res/layout/activity_main.xml @@ -14,6 +14,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" + android:choiceMode="singleChoice" android:listSelector="@drawable/bkg" > @@ -28,13 +29,15 @@ android:layout_height="wrap_content" android:layout_weight="1" android:onClick="makeCall" + android:importantForAccessibility = "no" android:src="@android:drawable/ic_menu_call" /> + android:text=" " + android:importantForAccessibility = "no"/> + android:src="@android:drawable/ic_menu_add" + android:importantForAccessibility = "no" /> diff --git a/pjsip-apps/src/swig/java/android/app/src/main/res/layout/dlg_account_config.xml b/pjsip-apps/src/swig/java/android/app/src/main/res/layout/dlg_account_config.xml index 6e64ea9e39..bc658e82d3 100644 --- a/pjsip-apps/src/swig/java/android/app/src/main/res/layout/dlg_account_config.xml +++ b/pjsip-apps/src/swig/java/android/app/src/main/res/layout/dlg_account_config.xml @@ -8,70 +8,76 @@ + android:paddingBottom="20dp" + android:textColor="#b0b0b0" + android:importantForAccessibility = "no"> - - - - - + + + - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - - + + + + + + - + + + + + + - - + + + + + + + + + + diff --git a/pjsip-apps/src/swig/java/android/app/src/main/res/layout/dlg_add_buddy.xml b/pjsip-apps/src/swig/java/android/app/src/main/res/layout/dlg_add_buddy.xml index 86617fcc1e..a7947bb946 100644 --- a/pjsip-apps/src/swig/java/android/app/src/main/res/layout/dlg_add_buddy.xml +++ b/pjsip-apps/src/swig/java/android/app/src/main/res/layout/dlg_add_buddy.xml @@ -1,25 +1,26 @@ - - - - - + xmlns:android="http://schemas.android.com/apk/res/android" + android:padding = "20dp" + android:layout_width="match_parent" + android:layout_height="match_parent"> - - - - - - + + + + + + + + + + + diff --git a/pjsip-apps/src/swig/java/android/app/src/main/res/menu/call.xml b/pjsip-apps/src/swig/java/android/app/src/main/res/menu/call.xml index d122a4b754..a31c16c00b 100644 --- a/pjsip-apps/src/swig/java/android/app/src/main/res/menu/call.xml +++ b/pjsip-apps/src/swig/java/android/app/src/main/res/menu/call.xml @@ -1,9 +1,10 @@ - + diff --git a/pjsip-apps/src/swig/java/android/app/src/main/res/menu/main.xml b/pjsip-apps/src/swig/java/android/app/src/main/res/menu/main.xml index be94829a2b..931f06dbe9 100644 --- a/pjsip-apps/src/swig/java/android/app/src/main/res/menu/main.xml +++ b/pjsip-apps/src/swig/java/android/app/src/main/res/menu/main.xml @@ -1,14 +1,14 @@ - + - diff --git a/pjsip-apps/src/swig/java/android/build.gradle b/pjsip-apps/src/swig/java/android/build.gradle index 0bf7b7f0c1..c35a60c66d 100644 --- a/pjsip-apps/src/swig/java/android/build.gradle +++ b/pjsip-apps/src/swig/java/android/build.gradle @@ -8,7 +8,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:7.4.2' + classpath 'com.android.tools.build:gradle:8.9.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/pjsip-apps/src/swig/java/android/gradle.properties b/pjsip-apps/src/swig/java/android/gradle.properties new file mode 100644 index 0000000000..20d0c69223 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/gradle.properties @@ -0,0 +1,3 @@ +android.useAndroidX=true +android.enableJetifier=true +android.nonFinalResIds=false diff --git a/pjsip-apps/src/swig/java/android/gradle/wrapper/gradle-wrapper.properties b/pjsip-apps/src/swig/java/android/gradle/wrapper/gradle-wrapper.properties index 5264804ef5..de0a6e27bc 100644 --- a/pjsip-apps/src/swig/java/android/gradle/wrapper/gradle-wrapper.properties +++ b/pjsip-apps/src/swig/java/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Dec 28 10:00:20 PST 2015 +#Thu Mar 13 11:32:10 WIB 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/pjsip-apps/src/swig/java/android/pjsua2/build.gradle b/pjsip-apps/src/swig/java/android/pjsua2/build.gradle index 3e292e5f9f..d044213834 100644 --- a/pjsip-apps/src/swig/java/android/pjsua2/build.gradle +++ b/pjsip-apps/src/swig/java/android/pjsua2/build.gradle @@ -19,8 +19,7 @@ android { arguments "-DANDROID_STL=c++_shared" } } - - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" } From 43a652a2335446d410fd243fe0812bb2d3162ebd Mon Sep 17 00:00:00 2001 From: Amilcar Ubiera Date: Wed, 9 Apr 2025 01:26:21 -0400 Subject: [PATCH 235/491] pool_i.h: Fix to warning for signed/unsigned mismatch. (#4389) --- pjlib/include/pj/pool_i.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pjlib/include/pj/pool_i.h b/pjlib/include/pj/pool_i.h index 6500c1589e..6226a548d2 100644 --- a/pjlib/include/pj/pool_i.h +++ b/pjlib/include/pj/pool_i.h @@ -57,7 +57,7 @@ PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_block *block, pj_size_t alignme //} ptr = PJ_POOL_ALIGN_PTR(block->cur, alignment); if (block->cur <= ptr && /* check pointer overflow */ - block->end - ptr >= size) /* check available size */ + (pj_size_t)(block->end - ptr) >= size) /* check available size */ { block->cur = ptr + size; return ptr; From 67371343a6aae41233fe57a4463dcea62eb22535 Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 9 Apr 2025 13:50:10 +0800 Subject: [PATCH 236/491] Add AVI writer (#4385) --- pjmedia/build/Makefile | 2 +- pjmedia/build/pjmedia.vcproj | 4 + pjmedia/build/pjmedia.vcxproj | 1 + pjmedia/build/pjmedia.vcxproj.filters | 3 + pjmedia/include/pjmedia/avi.h | 42 ++ pjmedia/include/pjmedia/avi_stream.h | 62 ++- pjmedia/include/pjmedia/signatures.h | 3 +- pjmedia/src/pjmedia/avi_player.c | 74 +--- pjmedia/src/pjmedia/avi_writer.c | 503 ++++++++++++++++++++++++ pjsip-apps/src/pjsua/pjsua_app.c | 93 +++++ pjsip-apps/src/pjsua/pjsua_app_common.h | 10 + pjsip-apps/src/pjsua/pjsua_app_config.c | 48 ++- 12 files changed, 788 insertions(+), 57 deletions(-) create mode 100644 pjmedia/src/pjmedia/avi_writer.c diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile index 2b9b9af02e..bec814c3ad 100644 --- a/pjmedia/build/Makefile +++ b/pjmedia/build/Makefile @@ -58,7 +58,7 @@ export _LDFLAGS := $(APP_THIRD_PARTY_LIBS) \ # export PJMEDIA_SRCDIR = ../src/pjmedia export PJMEDIA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ - alaw_ulaw.o alaw_ulaw_table.o avi_player.o av_sync.o \ + alaw_ulaw.o alaw_ulaw_table.o avi_player.o avi_writer.o av_sync.o \ bidirectional.o clock_thread.o codec.o conference.o \ conf_switch.o converter.o converter_libswscale.o converter_libyuv.o \ delaybuf.o echo_common.o \ diff --git a/pjmedia/build/pjmedia.vcproj b/pjmedia/build/pjmedia.vcproj index 3a28d64fea..2e0d00e6a2 100644 --- a/pjmedia/build/pjmedia.vcproj +++ b/pjmedia/build/pjmedia.vcproj @@ -3350,6 +3350,10 @@ RelativePath="..\src\pjmedia\avi_player.c" > + + diff --git a/pjmedia/build/pjmedia.vcxproj b/pjmedia/build/pjmedia.vcxproj index b8aed5ff71..927515ccb7 100644 --- a/pjmedia/build/pjmedia.vcxproj +++ b/pjmedia/build/pjmedia.vcxproj @@ -610,6 +610,7 @@ + diff --git a/pjmedia/build/pjmedia.vcxproj.filters b/pjmedia/build/pjmedia.vcxproj.filters index 4aa545eafa..c9e512dd51 100644 --- a/pjmedia/build/pjmedia.vcxproj.filters +++ b/pjmedia/build/pjmedia.vcxproj.filters @@ -20,6 +20,9 @@ Source Files + + Source Files + Source Files diff --git a/pjmedia/include/pjmedia/avi.h b/pjmedia/include/pjmedia/avi.h index c89c08b28c..15daa5af9b 100644 --- a/pjmedia/include/pjmedia/avi.h +++ b/pjmedia/include/pjmedia/avi.h @@ -191,6 +191,48 @@ typedef struct pjmedia_avi_subchunk } pjmedia_avi_subchunk; +/** + * Internal function to normalize data from AVI's little endian to host + * byte order and vice versa. + */ +#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 + static void pjmedia_avi_swap_data(void *data, pj_uint8_t bits, + unsigned count) + { + unsigned i; + + count /= (bits == 32? 4 : 2); + + if (bits == 32) { + pj_int32_t *data32 = (pj_int32_t *)data; + for (i = 0; i < count; ++i) + data32[i] = pj_swap32(data32[i]); + } else if (bits == 16) { + pj_int16_t *data16 = (pj_int16_t *)data; + for (i = 0; i < count; ++i) + data16[i] = pj_swap16(data16[i]); + } + + } + static void pjmedia_avi_swap_data2(void *data, pj_uint8_t nsizes, + pj_uint8_t *sizes) + { + unsigned i; + pj_int8_t *datap = (pj_int8_t *)data; + for (i = 0; i < nsizes; i++) { + pjmedia_avi_swap_data(datap, 32, sizes[i]); + datap += sizes[i++]; + if (i >= nsizes) + break; + pjmedia_avi_swap_data(datap, 16, sizes[i]); + datap += sizes[i]; + } + } +#else +# define pjmedia_avi_swap_data(data, bits, count) +# define pjmedia_avi_swap_data2(data, nsizes, sizes) +#endif + PJ_END_DECL /** diff --git a/pjmedia/include/pjmedia/avi_stream.h b/pjmedia/include/pjmedia/avi_stream.h index ada43eb77f..ac50728a0d 100644 --- a/pjmedia/include/pjmedia/avi_stream.h +++ b/pjmedia/include/pjmedia/avi_stream.h @@ -25,7 +25,6 @@ #include - PJ_BEGIN_DECL @@ -65,6 +64,13 @@ typedef pjmedia_port pjmedia_avi_stream; */ typedef struct pjmedia_avi_streams pjmedia_avi_streams; +struct pjmedia_avi_streams +{ + pj_pool_t *pool; + unsigned num_streams; + pjmedia_port **streams; +}; + /** * Create avi streams to play an AVI file. AVI player supports * reading AVI file with uncompressed video format and @@ -201,6 +207,60 @@ pjmedia_avi_stream_set_eof_cb2(pjmedia_avi_stream *stream, * @} */ +/** + * @defgroup PJMEDIA_AVI_FILE_WRITE AVI File Writer + * @ingroup PJMEDIA_PORT + * @brief Video and audio recording to AVI file + * @{ + */ + +/** + * Create avi streams to write to an AVI file. AVI writer supports + * recording AVI file with uncompressed video format and + * 16 bit PCM. + * + * Note that video recording file size can grow very quickly, and + * once it reaches the maximum size specified, the file will be + * automatically closed and the callback (if any) will be called. + * + * @param pool Pool to create the streams. + * @param filename File name to write to. + * @param max_fsize Maximum file size. + * @param num_streams Number of streams to write. Typically this should be + * 2, one for video, and one for audio. + * @param format The format of the streams. + * @param flags Avi streams creation flags. Currently must be zero. + * @param p_streams Pointer to receive the avi streams instance. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_avi_writer_create_streams(pj_pool_t *pool, + const char *filename, + pj_uint32_t max_fsize, + unsigned num_streams, + const pjmedia_format format[], + unsigned flags, + pjmedia_avi_streams **p_streams); + + +/** + * Register the callback to be called when the file writing has reached + * maximum size. + * + * @param streams The AVI writer streams. + * @param user_data User data to be specified in the callback, and will be + * given on the callback. + * @param cb Callback to be called. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_avi_streams_set_cb(pjmedia_avi_streams *streams, + void *user_data, + void (*cb)(pjmedia_avi_streams *streams, + void *usr_data)); + PJ_END_DECL diff --git a/pjmedia/include/pjmedia/signatures.h b/pjmedia/include/pjmedia/signatures.h index e9d12454c4..ee52208784 100644 --- a/pjmedia/include/pjmedia/signatures.h +++ b/pjmedia/include/pjmedia/signatures.h @@ -170,8 +170,9 @@ PJ_INLINE(const char*) pjmedia_sig_name(pjmedia_obj_sig sig, char buf[]) #define PJMEDIA_SIG_CLASS_PORT_VID(c,d) PJMEDIA_SIG_CLASS_PORT('V',c,d) #define PJMEDIA_SIG_IS_CLASS_PORT_VID(s) ((s)>>24=='P' && (((s)>>16)&0xff)=='V') -/** AVI player signature. */ +/** AVI player and writer signature. */ #define PJMEDIA_SIG_PORT_VID_AVI_PLAYER PJMEDIA_SIG_CLASS_PORT_VID('A','V') +#define PJMEDIA_SIG_PORT_VID_AVI_WRITER PJMEDIA_SIG_CLASS_PORT_VID('A','W') #define PJMEDIA_SIG_PORT_VID_STREAM PJMEDIA_SIG_CLASS_PORT_VID('S','T') #define PJMEDIA_SIG_PORT_VID_TEE PJMEDIA_SIG_CLASS_PORT_VID('T','E') diff --git a/pjmedia/src/pjmedia/avi_player.c b/pjmedia/src/pjmedia/avi_player.c index 1ea599b10e..45bb787a6b 100644 --- a/pjmedia/src/pjmedia/avi_player.c +++ b/pjmedia/src/pjmedia/avi_player.c @@ -60,42 +60,8 @@ # define TRACE_(x) #endif -#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 - static void data_to_host(void *data, pj_uint8_t bits, unsigned count) - { - unsigned i; - - count /= (bits == 32? 4 : 2); - - if (bits == 32) { - pj_int32_t *data32 = (pj_int32_t *)data; - for (i=0; i= nsizes) - break; - data_to_host(datap, 16, sizes[i]); - datap += sizes[i]; - } - } -#else -# define data_to_host(data, bits, count) -# define data_to_host2(data, nsizes, sizes) -#endif +#define data_to_host pjmedia_avi_swap_data +#define data_to_host2 pjmedia_avi_swap_data2 typedef struct avi_fmt_info { @@ -117,16 +83,14 @@ static avi_fmt_info avi_fmts[] = {PJMEDIA_FORMAT_PACK('D','X','5','0'), PJMEDIA_FORMAT_MPEG4} }; -struct pjmedia_avi_streams +typedef struct avi_reader_streams { - pj_pool_t *pool; - unsigned num_streams; - pjmedia_port **streams; + pjmedia_avi_streams base; /* AV synchronization */ pjmedia_av_sync *avsync; pj_size_t eof_cnt; -}; +} avi_reader_streams; struct avi_reader_port { @@ -148,7 +112,7 @@ struct avi_reader_port /* AV synchronization */ pjmedia_av_sync_media *avsync_media; pj_size_t slow_down_frm; - pjmedia_avi_streams *avi_streams; + avi_reader_streams *avi_streams; pj_status_t (*cb)(pjmedia_port*, void*); pj_bool_t subscribed; @@ -214,11 +178,11 @@ static pj_status_t file_read3(pj_oshandle_t fd, void *data, pj_ssize_t size, static void streams_on_destroy(void *arg) { - pjmedia_avi_streams *streams = (pjmedia_avi_streams*)arg; + avi_reader_streams *streams = (avi_reader_streams *)arg; if (streams->avsync) pjmedia_av_sync_destroy(streams->avsync); - pj_pool_safe_release(&streams->pool); + pj_pool_safe_release(&streams->base.pool); } @@ -245,6 +209,7 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool_, unsigned options, pjmedia_avi_streams **p_streams) { + avi_reader_streams *streams = NULL; pjmedia_avi_hdr avi_hdr; struct avi_reader_port *fport[PJMEDIA_AVI_MAX_NUM_STREAMS]; pj_off_t pos; @@ -596,7 +561,8 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool_, } /* Done. */ - *p_streams = pj_pool_calloc(pool, 1, sizeof(pjmedia_avi_streams)); + streams = pj_pool_calloc(pool, 1, sizeof(avi_reader_streams)); + *p_streams = (pjmedia_avi_streams *)streams; (*p_streams)->num_streams = nstr; (*p_streams)->streams = pj_pool_calloc(pool, (*p_streams)->num_streams, sizeof(pjmedia_port *)); @@ -615,7 +581,7 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool_, if (status != PJ_SUCCESS) goto on_error; - (*p_streams)->avsync = avsync; + streams->avsync = avsync; for (i = 0; i < nstr; i++) { pjmedia_av_sync_media_setting med_setting; @@ -640,7 +606,7 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool_, goto on_error; /* Set pointer to AVI streams */ - fport[i]->avi_streams = *p_streams; + fport[i]->avi_streams = streams; } } @@ -667,12 +633,12 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool_, pjmedia_port_destroy(&fport[i]->base); } - if (*p_streams && (*p_streams)->avsync) { + if (streams && streams->avsync) { for (i = 0; i < nstr; i++) { if (fport[i]->avsync_media) pjmedia_av_sync_del_media(NULL, fport[i]->avsync_media); } - pjmedia_av_sync_destroy((*p_streams)->avsync); + pjmedia_av_sync_destroy(streams->avsync); } pj_pool_release(pool); @@ -1006,9 +972,10 @@ static pj_status_t avi_get_frame(pjmedia_port *this_port, /* If synchronized, wait all streams to EOF before rewinding */ if (fport->avsync_media) { - pjmedia_avi_streams *avi_streams = fport->avi_streams; + avi_reader_streams *avi_streams = fport->avi_streams; - rewind_now = (avi_streams->eof_cnt % avi_streams->num_streams)==0; + rewind_now = (avi_streams->eof_cnt % + avi_streams->base.num_streams)==0; if (rewind_now) { pj_timestamp ts_zero = {{0}}; pjmedia_av_sync_update_ref(fport->avsync_media, @@ -1222,10 +1189,11 @@ static pj_status_t avi_get_frame(pjmedia_port *this_port, /* Reset AV sync on the last stream encountering EOF */ if (fport->avsync_media) { - pjmedia_avi_streams* avi_streams = fport->avi_streams; + avi_reader_streams *avi_streams = fport->avi_streams; if (avi_streams->avsync && - (++avi_streams->eof_cnt % avi_streams->num_streams == 0)) + (++avi_streams->eof_cnt % + avi_streams->base.num_streams == 0)) { pjmedia_av_sync_reset(avi_streams->avsync); } diff --git a/pjmedia/src/pjmedia/avi_writer.c b/pjmedia/src/pjmedia/avi_writer.c new file mode 100644 index 0000000000..bb34daaad4 --- /dev/null +++ b/pjmedia/src/pjmedia/avi_writer.c @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2025 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) + +#define THIS_FILE "avi_writer.c" + +#define SIGNATURE PJMEDIA_SIG_PORT_VID_AVI_WRITER + +#define SET_TAG(tag) *((pj_uint32_t*)avi_tags[tag]) + +#if 0 +# define TRACE_(x) PJ_LOG(4,x) +#else +# define TRACE_(x) +#endif + +typedef struct avi_writer_streams +{ + pjmedia_avi_streams base; + unsigned options; + pjmedia_avi_hdr avi_hdr; + + pj_oshandle_t fd; + pj_size_t frame_cnt; + pj_size_t total; + pj_size_t max_size; + + void (*cb2)(pjmedia_avi_streams *, void *); + void *user_data; + pj_bool_t subscribed; +} avi_writer_streams; + +struct avi_port +{ + pjmedia_port base; + unsigned stream_id; + pj_size_t frame_cnt; + avi_writer_streams *streams; +}; + +static pj_status_t write_headers(avi_writer_streams *streams) +{ + unsigned i; + pj_ssize_t size; + pj_status_t status; + + /* Write AVI header. */ + size = sizeof(riff_hdr_t) + sizeof(avih_hdr_t); + status = pj_file_write(streams->fd, &streams->avi_hdr, &size); + if (status != PJ_SUCCESS) + return status; + streams->total += size; + + /* Write stream headers. */ + for (i = 0; i < streams->base.num_streams; i++) { + size = sizeof(strl_hdr_t); + status = pj_file_write(streams->fd, &streams->avi_hdr.strl_hdr[i], + &size); + if (status != PJ_SUCCESS) + return status; + streams->total += size; + + size = streams->base.streams[i]->info.fmt.type == PJMEDIA_TYPE_AUDIO? + sizeof(strf_audio_hdr_t): sizeof(strf_video_hdr_t); + status = pj_file_write(streams->fd, &streams->avi_hdr.strf_hdr[i], + &size); + if (status != PJ_SUCCESS) + return status; + streams->total += size; + } + + return PJ_SUCCESS; +} + +static pj_status_t file_on_event(pjmedia_event *event, + void *user_data) +{ + avi_writer_streams *streams = (avi_writer_streams *)user_data; + + /* Call callback. */ + if (event->type == PJMEDIA_EVENT_CALLBACK) { + if (streams->cb2) + (*streams->cb2)(&streams->base, streams->user_data); + } + + return PJ_SUCCESS; +} + +/* + * Put frame into file. + */ +static pj_status_t avi_put_frame(pjmedia_port *this_port, + pjmedia_frame *frame) +{ + struct avi_port *fport = (struct avi_port*)this_port; + pjmedia_avi_subchunk ch; + pj_ssize_t size; + pj_status_t status = PJ_SUCCESS; + + pj_assert(fport->base.info.signature == SIGNATURE); + + if (frame->size <= 0) + return PJ_SUCCESS; + + pj_grp_lock_acquire(fport->base.grp_lock); + + if (fport->streams->fd == (pj_oshandle_t) (pj_ssize_t)-1) + goto on_return; + + /* Check if we have reached maximum size. */ + if (fport->streams->total + frame->size + sizeof(pjmedia_avi_subchunk) > + fport->streams->max_size) + { + pj_off_t file_size; + unsigned i; + + PJ_LOG(4, (THIS_FILE, "AVI writer max size %zu reached", + fport->streams->max_size)); + + /* Set AVI header's file length and total frames. */ + status = pj_file_getpos(fport->streams->fd, &file_size); + if (status != PJ_SUCCESS) + goto on_return; + + fport->streams->avi_hdr.riff_hdr.file_len = (pj_uint32_t) + (file_size - 8); + pjmedia_avi_swap_data(&fport->streams->avi_hdr.riff_hdr.file_len, + sizeof(pj_uint32_t), 32); + fport->streams->avi_hdr.avih_hdr.tot_frames =fport->streams->frame_cnt; + pjmedia_avi_swap_data(&fport->streams->avi_hdr.avih_hdr.tot_frames, + sizeof(pj_uint32_t), 32); + + for (i = 0; i < fport->streams->base.num_streams; i++) { + pjmedia_avi_swap_data(&fport->streams->avi_hdr.strl_hdr[i].length, + sizeof(pj_uint32_t), 32); + } + + /* Rewrite headers. */ + status = pj_file_setpos(fport->streams->fd, 0, PJ_SEEK_SET); + if (status != PJ_SUCCESS) + goto on_return; + status = write_headers(fport->streams); + if (status != PJ_SUCCESS) + goto on_return; + + /* Close file. */ + pj_file_close(fport->streams->fd); + fport->streams->fd = (pj_oshandle_t)(pj_ssize_t)-1; + + /* Call callback. */ + if (fport->streams->cb2) { + if (!fport->streams->subscribed) { + status = pjmedia_event_subscribe(NULL, &file_on_event, + fport->streams, + fport->streams); + fport->streams->subscribed = (status == PJ_SUCCESS)? PJ_TRUE: + PJ_FALSE; + } + + if (fport->streams->subscribed) { + pjmedia_event event; + + pjmedia_event_init(&event, PJMEDIA_EVENT_CALLBACK, + NULL, fport->streams); + pjmedia_event_publish(NULL, fport->streams, &event, + PJMEDIA_EVENT_PUBLISH_POST_EVENT); + } + } + + goto on_return; + } + + /* Write subchunk header. */ + ch.id = 0; + ((char *)&ch.id)[0] = '0'; + ((char *)&ch.id)[1] = '0' + fport->stream_id; + ch.len = frame->size; + size = sizeof(ch); + pjmedia_avi_swap_data(&ch, sizeof(ch), 32); + + status = pj_file_write(fport->streams->fd, &ch, &size); + if (status != PJ_SUCCESS) + goto on_return; + fport->streams->total += size; + + /* Write subchunk data. */ + size = frame->size; + if (fport->base.info.fmt.type == PJMEDIA_TYPE_AUDIO) { + pjmedia_avi_swap_data(frame->buf, frame->size, + fport->base.info.fmt.det.aud.bits_per_sample); + } + status = pj_file_write(fport->streams->fd, frame->buf, &size); + if (status != PJ_SUCCESS) + goto on_return; + fport->streams->total += size; + TRACE_((THIS_FILE, "Writing %ld total:%zu", size, + fport->streams->total)); + + /* Increase frame length/count. */ + fport->streams->avi_hdr.strl_hdr[fport->stream_id].length++; + if (fport->base.info.fmt.type == PJMEDIA_TYPE_VIDEO) { + fport->streams->frame_cnt++; + } + +on_return: + pj_grp_lock_release(fport->base.grp_lock); + + return status; +} + +static void streams_on_destroy(void *arg) +{ + avi_writer_streams *streams = (avi_writer_streams *)arg; + + if (streams->subscribed) { + pjmedia_event_unsubscribe(NULL, &file_on_event, streams, streams); + streams->subscribed = PJ_FALSE; + } + + if (streams->fd != (pj_oshandle_t) (pj_ssize_t)-1) { + pj_file_close(streams->fd); + streams->fd = (pj_oshandle_t)(pj_ssize_t)-1; + } + + pj_pool_safe_release(&streams->base.pool); +} + +static pj_status_t avi_on_destroy(pjmedia_port *this_port) +{ + PJ_UNUSED_ARG(this_port); + + return PJ_SUCCESS; +} + +/* + * Create AVI writer streams. + */ +PJ_DEF(pj_status_t) +pjmedia_avi_writer_create_streams(pj_pool_t *pool_, + const char *filename, + pj_uint32_t max_fsize, + unsigned num_streams, + const pjmedia_format format[], + unsigned options, + pjmedia_avi_streams **p_streams) +{ + avi_writer_streams *streams = NULL; + pjmedia_avi_hdr avi_hdr; + pj_uint32_t tags[3]; + struct avi_port *fport[PJMEDIA_AVI_MAX_NUM_STREAMS]; + unsigned i; + pj_ssize_t size; + pj_pool_t *pool = NULL; + pj_grp_lock_t *grp_lock = NULL; + pj_status_t status = PJ_SUCCESS; + + /* Check arguments. */ + PJ_ASSERT_RETURN(pool_ && filename && p_streams, PJ_EINVAL); + PJ_ASSERT_RETURN(num_streams > 0 && + num_streams <= PJMEDIA_AVI_MAX_NUM_STREAMS, PJ_EINVAL); + + /* Create own pool */ + pool = pj_pool_create(pool_->factory, "aviwriter", 500, 500, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + + /* Create AVI streams. */ + streams = pj_pool_calloc(pool, 1, sizeof(avi_writer_streams)); + PJ_ASSERT_RETURN(streams, PJ_ENOMEM); + streams->options = options; + streams->max_size = max_fsize; + streams->base.num_streams = num_streams; + streams->base.streams = pj_pool_calloc(pool, streams->base.num_streams, + sizeof(pjmedia_port *)); + + /* Create group lock */ + status = pj_grp_lock_create(pool, NULL, &grp_lock); + if (status != PJ_SUCCESS) + goto on_error; + + /* Init AVI headers. */ + pj_bzero(&avi_hdr, sizeof(pjmedia_avi_hdr)); + avi_hdr.riff_hdr.riff = SET_TAG(PJMEDIA_AVI_RIFF_TAG); + avi_hdr.riff_hdr.file_len = 0; /* will be filled later */ + avi_hdr.riff_hdr.avi = SET_TAG(PJMEDIA_AVI_AVI_TAG); + + avi_hdr.avih_hdr.list_tag = SET_TAG(PJMEDIA_AVI_LIST_TAG); + avi_hdr.avih_hdr.list_sz = sizeof(avih_hdr_t) - 8; + avi_hdr.avih_hdr.hdrl_tag = SET_TAG(PJMEDIA_AVI_HDRL_TAG); + avi_hdr.avih_hdr.avih = SET_TAG(PJMEDIA_AVI_AVIH_TAG); + avi_hdr.avih_hdr.size = 56; /* sizeof MainAVIHeader */ + avi_hdr.avih_hdr.num_streams = num_streams; + avi_hdr.avih_hdr.tot_frames = 0; /* will be filled later */ + + for (i = 0; i < avi_hdr.avih_hdr.num_streams; i++) { + const pj_str_t name = pj_str("aviw"); + + /* Create AVI writer port. */ + fport[i] = PJ_POOL_ZALLOC_T(pool, struct avi_port); + if (!fport[i]) { + status = PJ_ENOMEM; + goto on_error; + } + streams->base.streams[i] = &fport[i]->base; + + /* Init AVI writer port. */ + fport[i]->stream_id = i; + fport[i]->streams = streams; + fport[i]->base.put_frame = &avi_put_frame; + fport[i]->base.on_destroy = &avi_on_destroy; + pjmedia_port_info_init2(&fport[i]->base.info, &name, SIGNATURE, + PJMEDIA_DIR_DECODING, &format[i]); + pjmedia_port_init_grp_lock(&fport[i]->base, pool, grp_lock); + + /* Populate AVI headers. */ + avi_hdr.strl_hdr[i].list_tag = SET_TAG(PJMEDIA_AVI_LIST_TAG); + avi_hdr.strl_hdr[i].list_sz = sizeof(strl_hdr_t) - 8; + avi_hdr.strl_hdr[i].strl_tag = SET_TAG(PJMEDIA_AVI_STRL_TAG); + avi_hdr.strl_hdr[i].strh = SET_TAG(PJMEDIA_AVI_STRH_TAG); + avi_hdr.strl_hdr[i].strh_size = 56; /* sizeof AVIStreamHeader */ + + avi_hdr.avih_hdr.list_sz += sizeof(strl_hdr_t); + + if (format[i].type == PJMEDIA_TYPE_VIDEO) { + pjmedia_video_format_detail *vfd; + const pjmedia_video_format_info *vfi; + strf_video_hdr_t *strf_hdr; + + vfd = pjmedia_format_get_video_format_detail(&format[i], PJ_TRUE); + vfi = pjmedia_get_video_format_info( + pjmedia_video_format_mgr_instance(), + format[i].id); + + avi_hdr.avih_hdr.list_sz += sizeof(strf_video_hdr_t); + avi_hdr.avih_hdr.usec_per_frame = 1000000 * vfd->fps.denum / + vfd->fps.num; + avi_hdr.avih_hdr.max_Bps = vfd->max_bps; + avi_hdr.avih_hdr.buf_size = vfd->size.w * vfd->size.h * 4; + avi_hdr.avih_hdr.width = vfd->size.w; + avi_hdr.avih_hdr.height = vfd->size.h; + + avi_hdr.strl_hdr[i].list_sz += sizeof(strf_video_hdr_t); + avi_hdr.strl_hdr[i].data_type = SET_TAG(PJMEDIA_AVI_VIDS_TAG); + avi_hdr.strl_hdr[i].codec = format[i].id; + avi_hdr.strl_hdr[i].rate = vfd->fps.num; + avi_hdr.strl_hdr[i].scale = vfd->fps.denum; + avi_hdr.strl_hdr[i].buf_size = avi_hdr.avih_hdr.buf_size; + avi_hdr.strl_hdr[i].length = 0; /* will be filled later */ + + strf_hdr = &avi_hdr.strf_hdr[i].strf_video_hdr; + strf_hdr->strf = SET_TAG(PJMEDIA_AVI_STRF_TAG); + strf_hdr->strf_size = sizeof(strf_video_hdr_t) - 8; + strf_hdr->biSize = strf_hdr->strf_size; + strf_hdr->biCompression = format[i].id; + strf_hdr->biWidth = vfd->size.w; + strf_hdr->biHeight = vfd->size.h; + strf_hdr->biPlanes = 1; + strf_hdr->biBitCount = vfi->bpp; + strf_hdr->biSizeImage = vfd->size.w * vfd->size.h * vfi->bpp / 8; + + /* Normalize header to AVI's little endian. */ + pjmedia_avi_swap_data(&avi_hdr.strl_hdr[i], sizeof(strl_hdr_t), 32); + pjmedia_avi_swap_data2(strf_hdr, PJ_ARRAY_SIZE(strf_video_hdr_sizes), + strf_video_hdr_sizes); + } else { + pjmedia_audio_format_detail *afd; + strf_audio_hdr_t *strf_hdr; + + afd = pjmedia_format_get_audio_format_detail(&format[i], PJ_TRUE); + + avi_hdr.strl_hdr[i].list_sz += sizeof(strf_audio_hdr_t); + avi_hdr.strl_hdr[i].data_type = SET_TAG(PJMEDIA_AVI_AUDS_TAG); + avi_hdr.strl_hdr[i].codec = format[i].id; + avi_hdr.strl_hdr[i].rate = afd->clock_rate; + avi_hdr.strl_hdr[i].scale = 1; + avi_hdr.strl_hdr[i].quality = -1; + avi_hdr.strl_hdr[i].buf_size = 0; + avi_hdr.strl_hdr[i].sample_size = afd->bits_per_sample / 8; + avi_hdr.strl_hdr[i].length = 0; /* will be filled later */ + + strf_hdr = &avi_hdr.strf_hdr[i].strf_audio_hdr; + strf_hdr->strf = SET_TAG(PJMEDIA_AVI_STRF_TAG); + strf_hdr->strf_size = sizeof(strf_audio_hdr_t) - 8; + strf_hdr->fmt_tag = 1; /* 1 for PCM */ + strf_hdr->nchannels = afd->channel_count; + strf_hdr->sample_rate = afd->clock_rate; + strf_hdr->block_align = afd->channel_count * afd->bits_per_sample / 8; + strf_hdr->bytes_per_sec = strf_hdr->sample_rate * strf_hdr->block_align; + strf_hdr->bits_per_sample = afd->bits_per_sample; + + /* Normalize header to AVI's little endian. */ + pjmedia_avi_swap_data(&avi_hdr.strl_hdr[i], sizeof(strl_hdr_t), 32); + pjmedia_avi_swap_data2(strf_hdr, PJ_ARRAY_SIZE(strf_audio_hdr_sizes), + strf_audio_hdr_sizes); + } + } + + /* Normalize header to AVI's little endian. */ + pjmedia_avi_swap_data(&avi_hdr, sizeof(riff_hdr_t)+sizeof(avih_hdr_t), 32); + streams->avi_hdr = avi_hdr; + + /* Open file in write mode. */ + status = pj_file_open(pool, filename, PJ_O_WRONLY | PJ_O_CLOEXEC, + &streams->fd); + if (status != PJ_SUCCESS) + goto on_error; + + /* Write headers. */ + status = write_headers(streams); + if (status != PJ_SUCCESS) + goto on_error; + + /* Write MOVI tag to indicate the beginning of data. */ + tags[0] = SET_TAG(PJMEDIA_AVI_LIST_TAG); + tags[1] = 4; + tags[2] = SET_TAG(PJMEDIA_AVI_MOVI_TAG); + size = sizeof(tags); + pjmedia_avi_swap_data(tags, size, 32); + status = pj_file_write(streams->fd, tags, &size); + if (status != PJ_SUCCESS) + goto on_error; + streams->total += size; + + /* Done. */ + *p_streams = (pjmedia_avi_streams *)streams; + (*p_streams)->pool = pool; + + status = pj_grp_lock_add_handler(grp_lock, NULL, *p_streams, + &streams_on_destroy); + if (status != PJ_SUCCESS) + goto on_error; + + PJ_LOG(4,(THIS_FILE, + "AVI file writer '%.*s' created with " + "%d media ports", + (int)fport[0]->base.info.name.slen, + fport[0]->base.info.name.ptr, + (*p_streams)->num_streams)); + + return PJ_SUCCESS; + +on_error: + if (streams && streams->fd) { + pj_file_close(streams->fd); + streams->fd = (pj_oshandle_t)(pj_ssize_t)-1; + } + + if (grp_lock) { + pjmedia_port_destroy(&fport[0]->base); + for (i = 1; i < num_streams; i++) + pjmedia_port_destroy(&fport[i]->base); + } + + pj_pool_release(pool); + + return status; +} + +PJ_DEF(pj_status_t) +pjmedia_avi_streams_set_cb(pjmedia_avi_streams *streams, + void *user_data, + void (*cb)(pjmedia_avi_streams *streams, + void *usr_data)) +{ + avi_writer_streams *aviw = (avi_writer_streams *)streams; + + PJ_ASSERT_RETURN(streams && cb, PJ_EINVAL); + + aviw->cb2 = cb; + aviw->user_data = user_data; + + return PJ_SUCCESS; +} + +#endif /* PJMEDIA_HAS_VIDEO */ diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c index 7afca22d8e..e8d6738086 100644 --- a/pjsip-apps/src/pjsua/pjsua_app.c +++ b/pjsip-apps/src/pjsua/pjsua_app.c @@ -423,6 +423,13 @@ static void on_call_audio_state(pjsua_call_info *ci, unsigned mi, pjsua_conf_connect(call_conf_slot, app_config.rec_port); } + /* Record audio into AVI, if desired */ + if (app_config.avi_auto_rec && app_config.avi_rec_audio && + app_config.avi_aud_slot != PJSUA_INVALID_ID) + { + pjsua_conf_connect(call_conf_slot, app_config.avi_aud_slot); + } + /* Stream a file, if desired */ if ((app_config.auto_play || app_config.auto_play_hangup) && app_config.wav_port != PJSUA_INVALID_ID) @@ -490,6 +497,14 @@ static void on_call_audio_state(pjsua_call_info *ci, unsigned mi, pjsua_conf_connect(call_conf_slot, app_config.rec_port); pjsua_conf_connect(0, app_config.rec_port); } + + /* Record audio into AVI, if desired */ + if (app_config.avi_auto_rec && app_config.avi_rec_audio && + app_config.avi_aud_slot != PJSUA_INVALID_ID) + { + pjsua_conf_connect(call_conf_slot, app_config.avi_aud_slot); + pjsua_conf_connect(0, app_config.avi_aud_slot); + } } } } @@ -503,6 +518,18 @@ static void on_call_video_state(pjsua_call_info *ci, unsigned mi, arrange_window(ci->media[mi].stream.vid.win_in); +#if PJMEDIA_HAS_VIDEO + if (app_config.avi_auto_rec && + app_config.avi_vid_slot != PJSUA_INVALID_ID) + { + pjsua_conf_port_id pid; + + pid = pjsua_call_get_vid_conf_port(ci->id, PJMEDIA_DIR_DECODING); + if (pid != PJSUA_INVALID_ID) + pjsua_vid_conf_connect(pid, app_config.avi_vid_slot, NULL); + } +#endif + PJ_UNUSED_ARG(has_error); } @@ -1216,6 +1243,15 @@ static void hangup_timeout_callback(pj_timer_heap_t *timer_heap, pjsua_call_hangup_all(); } +static void avi_writer_cb(pjmedia_avi_streams *streams, + void *usr_data) +{ + PJ_UNUSED_ARG(streams); + PJ_UNUSED_ARG(usr_data); + + PJ_LOG(4, (THIS_FILE, "AVI recording has completed")); +} + /* * A simple registrar, invoked by default_mod_on_rx_request() */ @@ -1676,6 +1712,47 @@ static pj_status_t app_init(void) } + if (app_config.avi_rec.slen) { +#if PJMEDIA_HAS_VIDEO + pjmedia_format fmt[2]; + pjmedia_avi_streams *streams; + pjmedia_port *aviw_port; + + pjmedia_format_init_video(&fmt[0], PJMEDIA_FORMAT_I420, + 320, 240, 15, 1); + pjmedia_format_init_audio(&fmt[1], PJMEDIA_FORMAT_PCM, + app_config.media_cfg.clock_rate, + app_config.media_cfg.channel_count, + 16, + app_config.media_cfg.audio_frame_ptime*1000, + 0, 0); + status = pjmedia_avi_writer_create_streams(app_config.pool, + app_config.avi_rec.ptr, + app_config.avi_rec_size, + 2, fmt, 0, &streams); + pj_assert(status == PJ_SUCCESS); + + pjmedia_avi_streams_set_cb(streams, NULL, &avi_writer_cb); + + app_config.avi_vid_port = (pjmedia_port *) + pjmedia_avi_streams_get_stream(streams, 0); + status = pjsua_vid_conf_add_port(app_config.pool, + app_config.avi_vid_port, NULL, + &app_config.avi_vid_slot); + pj_assert(status == PJ_SUCCESS); + + if (app_config.avi_rec_audio) { + app_config.avi_aud_port = (pjmedia_port *) + pjmedia_avi_streams_get_stream(streams, + 1); + status = pjsua_conf_add_port(app_config.pool, + app_config.avi_aud_port, + &app_config.avi_aud_slot); + pj_assert(status == PJ_SUCCESS); + } +#endif + } + /* Create AVI player virtual devices */ if (app_config.avi_cnt) { #if PJMEDIA_HAS_VIDEO && PJMEDIA_VIDEO_DEV_HAS_AVI @@ -2183,6 +2260,20 @@ static pj_status_t app_destroy(void) #endif } + /* Close avi writer */ +#if PJMEDIA_HAS_VIDEO + if (app_config.avi_vid_slot != PJSUA_INVALID_ID) { + pjsua_vid_conf_remove_port(app_config.avi_vid_slot); + pjmedia_port_destroy(app_config.avi_vid_port); + app_config.avi_vid_slot = PJSUA_INVALID_ID; + } +#endif + if (app_config.avi_aud_slot != PJSUA_INVALID_ID) { + pjsua_conf_remove_port(app_config.avi_aud_slot); + pjmedia_port_destroy(app_config.avi_aud_port); + app_config.avi_aud_slot = PJSUA_INVALID_ID; + } + /* Close ringback port */ if (app_config.ringback_port && app_config.ringback_slot != PJSUA_INVALID_ID) @@ -2250,6 +2341,8 @@ static pj_status_t app_destroy(void) pj_bzero(&app_config, sizeof(app_config)); app_config.wav_id = PJSUA_INVALID_ID; app_config.rec_id = PJSUA_INVALID_ID; + app_config.avi_vid_slot = PJSUA_INVALID_ID; + app_config.avi_aud_slot = PJSUA_INVALID_ID; if (use_cli) { app_config.use_cli = use_cli; diff --git a/pjsip-apps/src/pjsua/pjsua_app_common.h b/pjsip-apps/src/pjsua/pjsua_app_common.h index b1fbf2e1db..e0882360cd 100644 --- a/pjsip-apps/src/pjsua/pjsua_app_common.h +++ b/pjsip-apps/src/pjsua/pjsua_app_common.h @@ -159,6 +159,16 @@ typedef struct pjsua_app_config pj_bool_t avi_auto_play; int avi_def_idx; + /* AVI recording */ + pj_str_t avi_rec; + pj_uint32_t avi_rec_size; + pj_bool_t avi_rec_audio; + pj_bool_t avi_auto_rec; + pjsua_conf_port_id avi_vid_slot; + pjmedia_port *avi_vid_port; + pjsua_conf_port_id avi_aud_slot; + pjmedia_port *avi_aud_port; + /* CLI setting */ pj_bool_t use_cli; cli_cfg_t cli_cfg; diff --git a/pjsip-apps/src/pjsua/pjsua_app_config.c b/pjsip-apps/src/pjsua/pjsua_app_config.c index 5607ff7f93..989b7868e5 100644 --- a/pjsip-apps/src/pjsua/pjsua_app_config.c +++ b/pjsip-apps/src/pjsua/pjsua_app_config.c @@ -182,8 +182,13 @@ static void usage(void) puts (" --video Enable video"); puts (" --vcapture-dev=id Video capture device ID (default=-1)"); puts (" --vrender-dev=id Video render device ID (default=-2)"); - puts (" --play-avi=FILE Load this AVI as virtual capture device"); + puts (" --play-avi=FILE Load this AVI as virtual capture device."); + puts (" This can be specified multiple times."); puts (" --auto-play-avi Automatically play the AVI media to call"); + puts (" --rec-avi=FILE Record video to AVI file"); + puts (" --rec-avi-size=N Maximum AVI recording file size"); + puts (" --rec-avi-audio Include audio in the AVI recording"); + puts (" --auto-rec-avi Automatically record video"); #endif puts (""); @@ -413,6 +418,7 @@ static pj_status_t parse_args(int argc, char *argv[], OPT_TIMER, OPT_TIMER_SE, OPT_TIMER_MIN_SE, OPT_VIDEO, OPT_TEXT, OPT_TEXT_RED, OPT_EXTRA_AUDIO, OPT_VCAPTURE_DEV, OPT_VRENDER_DEV, OPT_PLAY_AVI, OPT_AUTO_PLAY_AVI, + OPT_REC_AVI, OPT_REC_AVI_SIZE, OPT_REC_AVI_AUDIO, OPT_AUTO_REC_AVI, OPT_USE_CLI, OPT_CLI_TELNET_PORT, OPT_DISABLE_CLI_CONSOLE }; struct pj_getopt_option long_options[] = { @@ -562,6 +568,10 @@ static pj_status_t parse_args(int argc, char *argv[], { "vrender-dev", 1, 0, OPT_VRENDER_DEV}, { "play-avi", 1, 0, OPT_PLAY_AVI}, { "auto-play-avi", 0, 0, OPT_AUTO_PLAY_AVI}, + { "rec-avi", 1, 0, OPT_REC_AVI}, + { "rec-avi-size", 1, 0, OPT_REC_AVI_SIZE}, + { "rec-avi-audio", 0, 0, OPT_REC_AVI_AUDIO}, + { "auto-rec-avi", 0, 0, OPT_AUTO_REC_AVI}, { "use-cli", 0, 0, OPT_USE_CLI}, { "cli-telnet-port", 1, 0, OPT_CLI_TELNET_PORT}, { "no-cli-console", 0, 0, OPT_DISABLE_CLI_CONSOLE}, @@ -1563,6 +1573,22 @@ static pj_status_t parse_args(int argc, char *argv[], app_config.avi_auto_play = PJ_TRUE; break; + case OPT_REC_AVI: + app_config.avi_rec = pj_str(pj_optarg); + break; + + case OPT_REC_AVI_SIZE: + app_config.avi_rec_size = atoi(pj_optarg); + break; + + case OPT_REC_AVI_AUDIO: + app_config.avi_rec_audio = PJ_TRUE; + break; + + case OPT_AUTO_REC_AVI: + app_config.avi_auto_rec = PJ_TRUE; + break; + case OPT_USE_CLI: cfg->use_cli = PJ_TRUE; break; @@ -1697,6 +1723,8 @@ static void default_config() cfg->playback_lat = PJMEDIA_SND_DEFAULT_PLAY_LATENCY; cfg->ringback_slot = PJSUA_INVALID_ID; cfg->ring_slot = PJSUA_INVALID_ID; + cfg->avi_vid_slot = PJSUA_INVALID_ID; + cfg->avi_aud_slot = PJSUA_INVALID_ID; for (i=0; iacc_cfg); ++i) pjsua_acc_config_default(&cfg->acc_cfg[i]); @@ -2335,6 +2363,24 @@ int write_settings(pjsua_app_config *config, char *buf, pj_size_t max) pj_ansi_snprintf(line, sizeof(line), "--auto-play-avi\n"); pj_strcat2(&cfg, line); } + if (config->avi_rec.slen) { + pj_ansi_snprintf(line, sizeof(line), "--rec-avi %s\n", + config->avi_rec.ptr); + pj_strcat2(&cfg, line); + } + if (config->avi_rec_size) { + pj_ansi_snprintf(line, sizeof(line), "--rec-avi-size %d\n", + config->avi_rec_size); + pj_strcat2(&cfg, line); + } + if (config->avi_rec_audio) { + pj_ansi_snprintf(line, sizeof(line), "--rec-avi-audio\n"); + pj_strcat2(&cfg, line); + } + if (config->avi_auto_rec) { + pj_ansi_snprintf(line, sizeof(line), "--auto-rec-avi\n"); + pj_strcat2(&cfg, line); + } /* ptime */ if (config->media_cfg.ptime) { From 9b84df04fd117b28d1e0561a36c9b4d0705ac6c9 Mon Sep 17 00:00:00 2001 From: jimying Date: Wed, 9 Apr 2025 13:50:44 +0800 Subject: [PATCH 237/491] Fix typos (#4387) --- pjlib/include/pj/string.h | 4 ++-- pjsip/include/pjsip/sip_transport.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pjlib/include/pj/string.h b/pjlib/include/pj/string.h index 2084711762..f2956018ae 100644 --- a/pjlib/include/pj/string.h +++ b/pjlib/include/pj/string.h @@ -575,7 +575,7 @@ PJ_DECL(pj_ssize_t) pj_strtok2(const pj_str_t *str, const char *delim, * Find the occurence of a substring substr in string str. * * @param str The string to search. - * @param substr The string to search fo. + * @param substr The string to search for. * * @return the pointer to the position of substr in str, or NULL. Note * that if str is not NULL terminated, the returned pointer @@ -588,7 +588,7 @@ PJ_DECL(char*) pj_strstr(const pj_str_t *str, const pj_str_t *substr); * both strings. * * @param str The string to search. - * @param substr The string to search fo. + * @param substr The string to search for. * * @return the pointer to the position of substr in str, or NULL. Note * that if str is not NULL terminated, the returned pointer diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h index 14c2e5c522..8c183e26e5 100644 --- a/pjsip/include/pjsip/sip_transport.h +++ b/pjsip/include/pjsip/sip_transport.h @@ -389,7 +389,7 @@ struct pjsip_rx_data /** Start of msg buffer. */ char *msg_buf; - /** Length fo message. */ + /** Length of message. */ int len; /** The parsed message, if any. */ From 001856e734e8ba2d6eddef04d65033eba183aa5c Mon Sep 17 00:00:00 2001 From: Amilcar Ubiera Date: Wed, 9 Apr 2025 02:13:41 -0400 Subject: [PATCH 238/491] Add support for G.722 as passthrough codec. (#4390) --- pjlib/include/pj/config_site_sample.h | 3 +++ pjmedia/include/pjmedia-codec/config.h | 17 +++++++++++++++++ pjmedia/include/pjmedia/format.h | 3 +++ pjmedia/src/pjmedia-codec/passthrough.c | 14 +++++++++----- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/pjlib/include/pj/config_site_sample.h b/pjlib/include/pj/config_site_sample.h index f188c0be6d..6626cf125a 100644 --- a/pjlib/include/pj/config_site_sample.h +++ b/pjlib/include/pj/config_site_sample.h @@ -234,6 +234,7 @@ #define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR 1 + #define PJMEDIA_HAS_PASSTHROUGH_CODEC_G722 0 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_G729 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC 1 @@ -263,6 +264,7 @@ #define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR 1 + #define PJMEDIA_HAS_PASSTHROUGH_CODEC_G722 0 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_G729 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC 1 @@ -289,6 +291,7 @@ #define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR 0 + #define PJMEDIA_HAS_PASSTHROUGH_CODEC_G722 0 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_G729 0 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC 0 diff --git a/pjmedia/include/pjmedia-codec/config.h b/pjmedia/include/pjmedia-codec/config.h index 5d7a15b43f..d4201afbea 100644 --- a/pjmedia/include/pjmedia-codec/config.h +++ b/pjmedia/include/pjmedia-codec/config.h @@ -319,6 +319,15 @@ # define PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR 1 #endif +/** + * Enable G.722 passthrough codec. + * + * Default: 1 + */ +#ifndef PJMEDIA_HAS_PASSTHROUGH_CODEC_G722 +# define PJMEDIA_HAS_PASSTHROUGH_CODEC_G722 1 +#endif + /** * Enable G.729 passthrough codec. * @@ -372,6 +381,14 @@ # define PJMEDIA_HAS_G7221_CODEC 0 #endif +/* If passthrough and G.722 is enabled, disable the software + * G.722 codec + */ +#if PJMEDIA_HAS_PASSTHROUGH_CODECS && PJMEDIA_HAS_PASSTHROUGH_CODEC_G722 +# undef PJMEDIA_HAS_G722_CODEC +# define PJMEDIA_HAS_G722_CODEC 0 +#endif + /** * Enable OpenCORE AMR-NB codec. * See https://github.com/pjsip/pjproject/issues/1388 for some info. diff --git a/pjmedia/include/pjmedia/format.h b/pjmedia/include/pjmedia/format.h index 6c1b2d963a..d5e3f203d0 100644 --- a/pjmedia/include/pjmedia/format.h +++ b/pjmedia/include/pjmedia/format.h @@ -78,6 +78,9 @@ typedef enum pjmedia_format_id /** AMR narrowband */ PJMEDIA_FORMAT_AMR = PJMEDIA_FORMAT_PACK(' ', 'A', 'M', 'R'), + /** ITU G.722 */ + PJMEDIA_FORMAT_G722 = PJMEDIA_FORMAT_PACK('G', '7', '2', '2'), + /** ITU G.729 */ PJMEDIA_FORMAT_G729 = PJMEDIA_FORMAT_PACK('G', '7', '2', '9'), diff --git a/pjmedia/src/pjmedia-codec/passthrough.c b/pjmedia/src/pjmedia-codec/passthrough.c index 79db3bce8c..94c0118040 100644 --- a/pjmedia/src/pjmedia-codec/passthrough.c +++ b/pjmedia/src/pjmedia-codec/passthrough.c @@ -186,6 +186,13 @@ codec_desc[] = }, # endif +# if PJMEDIA_HAS_PASSTHROUGH_CODEC_G722 + {1, "G722", PJMEDIA_RTP_PT_G722, PJMEDIA_FORMAT_G722, + 16000, 1, 160, + 64000, 64000, 2, 1, 1 + }, +# endif + # if PJMEDIA_HAS_PASSTHROUGH_CODEC_G729 {1, "G729", PJMEDIA_RTP_PT_G729, PJMEDIA_FORMAT_G729, 8000, 1, 80, @@ -684,11 +691,8 @@ static pj_status_t codec_open( pjmedia_codec *codec, { codec_private_t *codec_data = (codec_private_t*) codec->codec_data; struct codec_desc *desc = &codec_desc[codec_data->codec_idx]; - pj_pool_t *pool; int i, j; - pool = codec_data->pool; - /* Cache samples per frame value */ codec_data->samples_per_frame = desc->samples_per_frame; @@ -770,7 +774,7 @@ static pj_status_t codec_open( pjmedia_codec *codec, } } - s = PJ_POOL_ZALLOC_T(pool, amr_settings_t); + s = PJ_POOL_ZALLOC_T(codec_data->pool, amr_settings_t); codec_data->codec_setting = s; s->enc_mode = enc_mode; @@ -839,7 +843,7 @@ static pj_status_t codec_open( pjmedia_codec *codec, */ if (enc_fmtp_mode != dec_fmtp_mode) { enc_fmtp_mode = dec_fmtp_mode = DEFAULT_MODE; - PJ_LOG(4,(pool->obj_name, + PJ_LOG(4,(codec_data->pool->obj_name, "Normalized iLBC encoder and decoder modes to %d", DEFAULT_MODE)); } From f94b18ef6e0c0b5d34eb274f85ac0a3b2cf9107a Mon Sep 17 00:00:00 2001 From: xiaoxiaoafeifei Date: Wed, 9 Apr 2025 14:49:27 +0800 Subject: [PATCH 239/491] Add support for the LoongArch64 architecture (#4386) --- aconfigure | 6 + aconfigure.ac | 6 + config.guess | 1788 ++++++---- config.sub | 3144 ++++++++++------- third_party/webrtc/PJSIP_NOTES | 18 +- third_party/webrtc/src/webrtc/typedefs.h | 3 + third_party/webrtc_aec3/PJSIP_NOTES | 16 + .../webrtc_aec3/src/rtc_base/system/arch.h | 3 + 8 files changed, 2941 insertions(+), 2043 deletions(-) diff --git a/aconfigure b/aconfigure index b583855b47..d1753873ea 100755 --- a/aconfigure +++ b/aconfigure @@ -11147,6 +11147,9 @@ printf "%s\n" "Checking if libwebrtc is disabled...no" >&6; } ac_webrtc_instset=neon ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" ;; + loongarch*) + ac_webrtc_instset=generic + ;; riscv*) ac_webrtc_instset=generic ;; @@ -11290,6 +11293,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_webrtc_aec3_instset=neon ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" ;; + loongarch*) + ac_webrtc_aec3_instset=generic + ;; riscv*) ac_webrtc_aec3_instset=generic ;; diff --git a/aconfigure.ac b/aconfigure.ac index 9e6752e021..1c21d432b3 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -2783,6 +2783,9 @@ AC_ARG_ENABLE(libwebrtc, ac_webrtc_instset=neon ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" ;; + loongarch*) + ac_webrtc_instset=generic + ;; riscv*) ac_webrtc_instset=generic ;; @@ -2893,6 +2896,9 @@ AC_ARG_ENABLE(libwebrtc_aec3, ac_webrtc_aec3_instset=neon ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" ;; + loongarch*) + ac_webrtc_aec3_instset=generic + ;; riscv*) ac_webrtc_aec3_instset=generic ;; diff --git a/config.guess b/config.guess index b6f948fdc5..48a684601b 100755 --- a/config.guess +++ b/config.guess @@ -1,14 +1,14 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -# 2011, 2012 Free Software Foundation, Inc. +# Copyright 1992-2024 Free Software Foundation, Inc. -timestamp='2012-06-17' +# shellcheck disable=SC2006,SC2268 # see below for rationale + +timestamp='2024-07-27' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or +# the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but @@ -17,33 +17,39 @@ timestamp='2012-06-17' # General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, see . +# along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - - -# Originally written by Per Bothner. Please send patches (context -# diff format) to and include a ChangeLog -# entry. +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). # -# This script attempts to guess a canonical system name similar to -# config.sub. If it succeeds, it prints the system name on stdout, and -# exits with 0. Otherwise, it exits with 1. +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess +# +# Please send patches to . + + +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] -Output the configuration name of the system \`$me' is run on. +Output the configuration name of the system '$me' is run on. -Operation modes: +Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit @@ -54,15 +60,13 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, -2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 -Free Software Foundation, Inc. +Copyright 1992-2024 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" -Try \`$me --help' for more information." +Try '$me --help' for more information." # Parse command line while test $# -gt 0 ; do @@ -90,57 +94,109 @@ if test $# != 0; then exit 1 fi -trap 'exit 1' 1 2 15 +# Just in case it came from the environment. +GUESS= # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. -# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still -# use `HOST_CC' if defined, but it is deprecated. +# Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still +# use 'HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. -set_cc_for_build=' -trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; -trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; -: ${TMPDIR=/tmp} ; - { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; -dummy=$tmp/dummy ; -tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; -case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int x;" > $dummy.c ; - for c in cc gcc c89 c99 ; do - if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then - CC_FOR_BUILD="$c"; break ; - fi ; - done ; - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found ; - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac ; set_cc_for_build= ;' +tmp= +# shellcheck disable=SC2172 +trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 + +set_cc_for_build() { + # prevent multiple calls if $tmp is already set + test "$tmp" && return 0 + : "${TMPDIR=/tmp}" + # shellcheck disable=SC2039,SC3028 + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } + dummy=$tmp/dummy + case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in + ,,) echo "int x;" > "$dummy.c" + for driver in cc gcc c17 c99 c89 ; do + if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD=$driver + break + fi + done + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; + esac +} # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then +if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown +case $UNAME_SYSTEM in +Linux|GNU|GNU/*) + LIBC=unknown + + set_cc_for_build + cat <<-EOF > "$dummy.c" + #if defined(__ANDROID__) + LIBC=android + #else + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #elif defined(__GLIBC__) + LIBC=gnu + #elif defined(__LLVM_LIBC__) + LIBC=llvm + #else + #include + /* First heuristic to detect musl libc. */ + #ifdef __DEFINED_va_list + LIBC=musl + #endif + #endif + #endif + EOF + cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + eval "$cc_set_libc" + + # Second heuristic to detect musl libc. + if [ "$LIBC" = unknown ] && + command -v ldd >/dev/null && + ldd --version 2>&1 | grep -q ^musl; then + LIBC=musl + fi + + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + if [ "$LIBC" = unknown ]; then + LIBC=gnu + fi + ;; +esac + # Note: order is significant - the case branches are not exclusive. -case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in +case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, @@ -152,22 +208,32 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". - sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || echo unknown)` - case "${UNAME_MACHINE_ARCH}" in + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + echo unknown)` + case $UNAME_MACHINE_ARCH in + aarch64eb) machine=aarch64_be-unknown ;; armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; - *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + earmv*) + arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` + machine=${arch}${endian}-unknown + ;; + *) machine=$UNAME_MACHINE_ARCH-unknown ;; esac # The Operating System including object format, if it has switched - # to ELF recently, or will in the future. - case "${UNAME_MACHINE_ARCH}" in + # to ELF recently (or will in the future) and ABI. + case $UNAME_MACHINE_ARCH in + earm*) + os=netbsdelf + ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval $set_cc_for_build + set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then @@ -182,45 +248,80 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in os=netbsd ;; esac + # Determine ABI tags. + case $UNAME_MACHINE_ARCH in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` + ;; + esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. - case "${UNAME_VERSION}" in + case $UNAME_VERSION in Debian*) release='-gnu' ;; *) - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}" - exit ;; + GUESS=$machine-${os}${release}${abi-} + ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE + ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE + ;; + *:SecBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE + ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE + ;; + *:MidnightBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE + ;; *:ekkoBSD:*:*) - echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE + ;; *:SolidBSD:*:*) - echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE + ;; + *:OS108:*:*) + GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE + ;; macppc:MirBSD:*:*) - echo powerpc-unknown-mirbsd${UNAME_RELEASE} - exit ;; + GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE + ;; *:MirBSD:*:*) - echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE + ;; + *:Sortix:*:*) + GUESS=$UNAME_MACHINE-unknown-sortix + ;; + *:Twizzler:*:*) + GUESS=$UNAME_MACHINE-unknown-twizzler + ;; + *:Redox:*:*) + GUESS=$UNAME_MACHINE-unknown-redox + ;; + mips:OSF1:*.*) + GUESS=mips-dec-osf1 + ;; alpha:OSF1:*:*) + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + trap '' 0 case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` @@ -234,163 +335,158 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` - case "$ALPHA_CPU_TYPE" in + case $ALPHA_CPU_TYPE in "EV4 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV4.5 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV5 (21164)") - UNAME_MACHINE="alphaev5" ;; + UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") - UNAME_MACHINE="alphaev56" ;; + UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") - UNAME_MACHINE="alphapca56" ;; + UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") - UNAME_MACHINE="alphapca57" ;; + UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") - UNAME_MACHINE="alphaev6" ;; + UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") - UNAME_MACHINE="alphaev67" ;; + UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") - UNAME_MACHINE="alphaev69" ;; + UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") - UNAME_MACHINE="alphaev7" ;; + UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") - UNAME_MACHINE="alphaev79" ;; + UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - # Reset EXIT trap before exiting to avoid spurious non-zero exit code. - exitcode=$? - trap '' 0 - exit $exitcode ;; - Alpha\ *:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # Should we change UNAME_MACHINE based on the output of uname instead - # of the specific Alpha model? - echo alpha-pc-interix - exit ;; - 21064:Windows_NT:50:3) - echo alpha-dec-winnt3.5 - exit ;; + OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + GUESS=$UNAME_MACHINE-dec-osf$OSF_REL + ;; Amiga*:UNIX_System_V:4.0:*) - echo m68k-unknown-sysv4 - exit ;; + GUESS=m68k-unknown-sysv4 + ;; *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-amigaos - exit ;; + GUESS=$UNAME_MACHINE-unknown-amigaos + ;; *:[Mm]orph[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-morphos - exit ;; + GUESS=$UNAME_MACHINE-unknown-morphos + ;; *:OS/390:*:*) - echo i370-ibm-openedition - exit ;; + GUESS=i370-ibm-openedition + ;; *:z/VM:*:*) - echo s390-ibm-zvmoe - exit ;; + GUESS=s390-ibm-zvmoe + ;; *:OS400:*:*) - echo powerpc-ibm-os400 - exit ;; + GUESS=powerpc-ibm-os400 + ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix${UNAME_RELEASE} - exit ;; - arm:riscos:*:*|arm:RISCOS:*:*) - echo arm-unknown-riscos - exit ;; + GUESS=arm-acorn-riscix$UNAME_RELEASE + ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + GUESS=arm-unknown-riscos + ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit ;; + GUESS=hppa1.1-hitachi-hiuxmpp + ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit ;; + case `(/bin/universe) 2>/dev/null` in + att) GUESS=pyramid-pyramid-sysv3 ;; + *) GUESS=pyramid-pyramid-bsd ;; + esac + ;; NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit ;; + GUESS=pyramid-pyramid-svr4 + ;; DRS?6000:unix:4.0:6*) - echo sparc-icl-nx6 - exit ;; + GUESS=sparc-icl-nx6 + ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in - sparc) echo sparc-icl-nx7; exit ;; - esac ;; + sparc) GUESS=sparc-icl-nx7 ;; + esac + ;; s390x:SunOS:*:*) - echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL + ;; sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-hal-solaris2$SUN_REL + ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris2$SUN_REL + ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) - echo i386-pc-auroraux${UNAME_RELEASE} - exit ;; + GUESS=i386-pc-auroraux$UNAME_RELEASE + ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - eval $set_cc_for_build - SUN_ARCH="i386" + set_cc_for_build + SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then - SUN_ARCH="x86_64" + SUN_ARCH=x86_64 fi fi - echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$SUN_ARCH-pc-solaris2$SUN_REL + ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris3$SUN_REL + ;; sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in + case `/usr/bin/arch -k` in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac - # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` - exit ;; + # Japanese Language versions have a version number like '4.1.3-JL'. + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` + GUESS=sparc-sun-sunos$SUN_REL + ;; sun3*:SunOS:*:*) - echo m68k-sun-sunos${UNAME_RELEASE} - exit ;; + GUESS=m68k-sun-sunos$UNAME_RELEASE + ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 - case "`/bin/arch`" in + test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 + case `/bin/arch` in sun3) - echo m68k-sun-sunos${UNAME_RELEASE} + GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun4) - echo sparc-sun-sunos${UNAME_RELEASE} + GUESS=sparc-sun-sunos$UNAME_RELEASE ;; esac - exit ;; + ;; aushp:SunOS:*:*) - echo sparc-auspex-sunos${UNAME_RELEASE} - exit ;; + GUESS=sparc-auspex-sunos$UNAME_RELEASE + ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor @@ -400,44 +496,44 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-milan-mint$UNAME_RELEASE + ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-hades-mint$UNAME_RELEASE + ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-unknown-mint$UNAME_RELEASE + ;; m68k:machten:*:*) - echo m68k-apple-machten${UNAME_RELEASE} - exit ;; + GUESS=m68k-apple-machten$UNAME_RELEASE + ;; powerpc:machten:*:*) - echo powerpc-apple-machten${UNAME_RELEASE} - exit ;; + GUESS=powerpc-apple-machten$UNAME_RELEASE + ;; RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit ;; + GUESS=mips-dec-mach_bsd4.3 + ;; RISC*:ULTRIX:*:*) - echo mips-dec-ultrix${UNAME_RELEASE} - exit ;; + GUESS=mips-dec-ultrix$UNAME_RELEASE + ;; VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix${UNAME_RELEASE} - exit ;; + GUESS=vax-dec-ultrix$UNAME_RELEASE + ;; 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix${UNAME_RELEASE} - exit ;; + GUESS=clipper-intergraph-clix$UNAME_RELEASE + ;; mips:*:*:UMIPS | mips:*:*:RISCos) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { @@ -446,98 +542,100 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF - $CC_FOR_BUILD -o $dummy $dummy.c && - dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && - SYSTEM_NAME=`$dummy $dummyarg` && + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && + dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos${UNAME_RELEASE} - exit ;; + GUESS=mips-mips-riscos$UNAME_RELEASE + ;; Motorola:PowerMAX_OS:*:*) - echo powerpc-motorola-powermax - exit ;; + GUESS=powerpc-motorola-powermax + ;; Motorola:*:4.3:PL8-*) - echo powerpc-harris-powermax - exit ;; + GUESS=powerpc-harris-powermax + ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) - echo powerpc-harris-powermax - exit ;; + GUESS=powerpc-harris-powermax + ;; Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit ;; + GUESS=powerpc-harris-powerunix + ;; m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit ;; + GUESS=m88k-harris-cxux7 + ;; m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit ;; + GUESS=m88k-motorola-sysv4 + ;; m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit ;; + GUESS=m88k-motorola-sysv3 + ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 then - if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ - [ ${TARGET_BINARY_INTERFACE}x = x ] + if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ + test "$TARGET_BINARY_INTERFACE"x = x then - echo m88k-dg-dgux${UNAME_RELEASE} + GUESS=m88k-dg-dgux$UNAME_RELEASE else - echo m88k-dg-dguxbcs${UNAME_RELEASE} + GUESS=m88k-dg-dguxbcs$UNAME_RELEASE fi else - echo i586-dg-dgux${UNAME_RELEASE} + GUESS=i586-dg-dgux$UNAME_RELEASE fi - exit ;; + ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit ;; + GUESS=m88k-dolphin-sysv3 + ;; M88*:*:R3*:*) # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit ;; + GUESS=m88k-motorola-sysv3 + ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit ;; + GUESS=m88k-tektronix-sysv3 + ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit ;; + GUESS=m68k-tektronix-bsd + ;; *:IRIX*:*:*) - echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` - exit ;; + IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` + GUESS=mips-sgi-irix$IRIX_REL + ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. - echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id + ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) - echo i386-ibm-aix - exit ;; + GUESS=i386-ibm-aix + ;; ia64:AIX:*:*) - if [ -x /usr/bin/oslevel ] ; then + if test -x /usr/bin/oslevel ; then IBM_REV=`/usr/bin/oslevel` else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi - echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} - exit ;; + GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV + ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #include - main() + int + main () { if (!__power_pc()) exit(1); @@ -545,82 +643,84 @@ EOF exit(0); } EOF - if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then - echo "$SYSTEM_NAME" + GUESS=$SYSTEM_NAME else - echo rs6000-ibm-aix3.2.5 + GUESS=rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 + GUESS=rs6000-ibm-aix3.2.4 else - echo rs6000-ibm-aix3.2 + GUESS=rs6000-ibm-aix3.2 fi - exit ;; + ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` - if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` + if test -x /usr/bin/lslpp ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \ + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi - echo ${IBM_ARCH}-ibm-aix${IBM_REV} - exit ;; + GUESS=$IBM_ARCH-ibm-aix$IBM_REV + ;; *:AIX:*:*) - echo rs6000-ibm-aix - exit ;; - ibmrt:4.4BSD:*|romp-ibm:BSD:*) - echo romp-ibm-bsd4.4 - exit ;; + GUESS=rs6000-ibm-aix + ;; + ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) + GUESS=romp-ibm-bsd4.4 + ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to - exit ;; # report: romp-ibm BSD 4.3 + GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to + ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) - echo rs6000-bull-bosx - exit ;; + GUESS=rs6000-bull-bosx + ;; DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit ;; + GUESS=m68k-bull-sysv3 + ;; 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit ;; + GUESS=m68k-hp-bsd + ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit ;; + GUESS=m68k-hp-bsd4.4 + ;; 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - case "${UNAME_MACHINE}" in - 9000/31? ) HP_ARCH=m68000 ;; - 9000/[34]?? ) HP_ARCH=m68k ;; + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + case $UNAME_MACHINE in + 9000/31?) HP_ARCH=m68000 ;; + 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) - if [ -x /usr/bin/getconf ]; then + if test -x /usr/bin/getconf; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "${sc_cpu_version}" in - 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 - 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + case $sc_cpu_version in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 - case "${sc_kernel_bits}" in - 32) HP_ARCH="hppa2.0n" ;; - 64) HP_ARCH="hppa2.0w" ;; - '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + case $sc_kernel_bits in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi - if [ "${HP_ARCH}" = "" ]; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + if test "$HP_ARCH" = ""; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include #include - int main () + int + main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); @@ -647,13 +747,13 @@ EOF exit (0); } EOF - (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac - if [ ${HP_ARCH} = "hppa2.0w" ] + if test "$HP_ARCH" = hppa2.0w then - eval $set_cc_for_build + set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler @@ -664,23 +764,23 @@ EOF # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 - if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then - HP_ARCH="hppa2.0w" + HP_ARCH=hppa2.0w else - HP_ARCH="hppa64" + HP_ARCH=hppa64 fi fi - echo ${HP_ARCH}-hp-hpux${HPUX_REV} - exit ;; + GUESS=$HP_ARCH-hp-hpux$HPUX_REV + ;; ia64:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux${HPUX_REV} - exit ;; + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + GUESS=ia64-hp-hpux$HPUX_REV + ;; 3050*:HI-UX:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #include int main () @@ -705,38 +805,38 @@ EOF exit (0); } EOF - $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } - echo unknown-hitachi-hiuxwe2 - exit ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) - echo hppa1.1-hp-bsd - exit ;; + GUESS=unknown-hitachi-hiuxwe2 + ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) + GUESS=hppa1.1-hp-bsd + ;; 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit ;; + GUESS=hppa1.0-hp-bsd + ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) - echo hppa1.1-hp-osf - exit ;; + GUESS=hppa1.0-hp-mpeix + ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) + GUESS=hppa1.1-hp-osf + ;; hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit ;; + GUESS=hppa1.0-hp-osf + ;; i*86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-unknown-osf1mk + if test -x /usr/sbin/sysversion ; then + GUESS=$UNAME_MACHINE-unknown-osf1mk else - echo ${UNAME_MACHINE}-unknown-osf1 + GUESS=$UNAME_MACHINE-unknown-osf1 fi - exit ;; + ;; parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit ;; + GUESS=hppa1.1-hp-lites + ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit ;; + GUESS=c1-convex-bsd + ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd @@ -744,136 +844,174 @@ EOF fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit ;; + GUESS=c34-convex-bsd + ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit ;; + GUESS=c38-convex-bsd + ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit ;; + GUESS=c4-convex-bsd + ;; CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=ymp-cray-unicos$CRAY_REL + ;; CRAY*[A-Z]90:*:*:*) - echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) - echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=t90-cray-unicos$CRAY_REL + ;; CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=alphaev5-cray-unicosmk$CRAY_REL + ;; CRAY*SV1:*:*:*) - echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=sv1-cray-unicos$CRAY_REL + ;; *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=craynv-cray-unicosmp$CRAY_REL + ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` - echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` + GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` - echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE + ;; sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi${UNAME_RELEASE} - exit ;; + GUESS=sparc-unknown-bsdi$UNAME_RELEASE + ;; *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE + ;; + arm:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + set_cc_for_build + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi + else + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf + fi + ;; *:FreeBSD:*:*) - UNAME_PROCESSOR=`/usr/bin/uname -p` - case ${UNAME_PROCESSOR} in + UNAME_PROCESSOR=`uname -p` + case $UNAME_PROCESSOR in amd64) - echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - *) - echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; esac - exit ;; + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL + ;; i*:CYGWIN*:*) - echo ${UNAME_MACHINE}-pc-cygwin - exit ;; + GUESS=$UNAME_MACHINE-pc-cygwin + ;; + *:MINGW64*:*) + GUESS=$UNAME_MACHINE-pc-mingw64 + ;; *:MINGW*:*) - echo ${UNAME_MACHINE}-pc-mingw32 - exit ;; - i*:MSYS*:*) - echo ${UNAME_MACHINE}-pc-msys - exit ;; - i*:windows32*:*) - # uname -m includes "-pc" on this system. - echo ${UNAME_MACHINE}-mingw32 - exit ;; + GUESS=$UNAME_MACHINE-pc-mingw32 + ;; + *:MSYS*:*) + GUESS=$UNAME_MACHINE-pc-msys + ;; i*:PW*:*) - echo ${UNAME_MACHINE}-pc-pw32 - exit ;; + GUESS=$UNAME_MACHINE-pc-pw32 + ;; + *:SerenityOS:*:*) + GUESS=$UNAME_MACHINE-pc-serenity + ;; *:Interix*:*) - case ${UNAME_MACHINE} in + case $UNAME_MACHINE in x86) - echo i586-pc-interix${UNAME_RELEASE} - exit ;; + GUESS=i586-pc-interix$UNAME_RELEASE + ;; authenticamd | genuineintel | EM64T) - echo x86_64-unknown-interix${UNAME_RELEASE} - exit ;; + GUESS=x86_64-unknown-interix$UNAME_RELEASE + ;; IA64) - echo ia64-unknown-interix${UNAME_RELEASE} - exit ;; + GUESS=ia64-unknown-interix$UNAME_RELEASE + ;; esac ;; - [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) - echo i${UNAME_MACHINE}-pc-mks - exit ;; - 8664:Windows_NT:*) - echo x86_64-pc-mks - exit ;; - i*:Windows_NT*:* | Pentium*:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we - # UNAME_MACHINE based on the output of uname instead of i386? - echo i586-pc-interix - exit ;; i*:UWIN*:*) - echo ${UNAME_MACHINE}-pc-uwin - exit ;; + GUESS=$UNAME_MACHINE-pc-uwin + ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-unknown-cygwin - exit ;; - p*:CYGWIN*:*) - echo powerpcle-unknown-cygwin - exit ;; + GUESS=x86_64-pc-cygwin + ;; prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=powerpcle-unknown-solaris2$SUN_REL + ;; *:GNU:*:*) # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` - exit ;; + GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` + GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL + ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu - exit ;; - i*86:Minix:*:*) - echo ${UNAME_MACHINE}-pc-minix - exit ;; + GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC + ;; + x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*) + GUESS="$UNAME_MACHINE-pc-managarm-mlibc" + ;; + *:[Mm]anagarm:*:*) + GUESS="$UNAME_MACHINE-unknown-managarm-mlibc" + ;; + *:Minix:*:*) + GUESS=$UNAME_MACHINE-unknown-minix + ;; aarch64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + set_cc_for_build + CPU=$UNAME_MACHINE + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __ARM_EABI__ + #ifdef __ARM_PCS_VFP + ABI=eabihf + #else + ABI=eabi + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;; + esac + fi + GUESS=$CPU-unknown-linux-$LIBCABI + ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; @@ -883,171 +1021,246 @@ EOF EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi - echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} - exit ;; + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; arm*:Linux:*:*) - eval $set_cc_for_build + set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then - echo ${UNAME_MACHINE}-unknown-linux-gnu + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then - echo ${UNAME_MACHINE}-unknown-linux-gnueabi + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi else - echo ${UNAME_MACHINE}-unknown-linux-gnueabihf + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf fi fi - exit ;; + ;; avr32*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; cris:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; crisv32:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; + e2k:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; frv:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; hexagon:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; i*86:Linux:*:*) - LIBC=gnu - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #ifdef __dietlibc__ - LIBC=dietlibc - #endif -EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` - echo "${UNAME_MACHINE}-pc-linux-${LIBC}" - exit ;; + GUESS=$UNAME_MACHINE-pc-linux-$LIBC + ;; ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + k1om:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + kvx:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + kvx:cos:*:*) + GUESS=$UNAME_MACHINE-unknown-cos + ;; + kvx:mbr:*:*) + GUESS=$UNAME_MACHINE-unknown-mbr + ;; + loongarch32:Linux:*:* | loongarch64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; mips:Linux:*:* | mips64:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + IS_GLIBC=0 + test x"${LIBC}" = xgnu && IS_GLIBC=1 + sed 's/^ //' << EOF > "$dummy.c" #undef CPU - #undef ${UNAME_MACHINE} - #undef ${UNAME_MACHINE}el + #undef mips + #undef mipsel + #undef mips64 + #undef mips64el + #if ${IS_GLIBC} && defined(_ABI64) + LIBCABI=gnuabi64 + #else + #if ${IS_GLIBC} && defined(_ABIN32) + LIBCABI=gnuabin32 + #else + LIBCABI=${LIBC} + #endif + #endif + + #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa64r6 + #else + #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa32r6 + #else + #if defined(__mips64) + CPU=mips64 + #else + CPU=mips + #endif + #endif + #endif + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=${UNAME_MACHINE}el + MIPS_ENDIAN=el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=${UNAME_MACHINE} + MIPS_ENDIAN= #else - CPU= + MIPS_ENDIAN= #endif #endif EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` + eval "$cc_set_vars" + test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } + ;; + mips64el:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + openrisc*:Linux:*:*) + GUESS=or1k-unknown-linux-$LIBC + ;; + or32:Linux:*:* | or1k*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; - or32:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; padre:Linux:*:*) - echo sparc-unknown-linux-gnu - exit ;; + GUESS=sparc-unknown-linux-$LIBC + ;; parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-gnu - exit ;; + GUESS=hppa64-unknown-linux-$LIBC + ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-gnu ;; - PA8*) echo hppa2.0-unknown-linux-gnu ;; - *) echo hppa-unknown-linux-gnu ;; + PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; + PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; + *) GUESS=hppa-unknown-linux-$LIBC ;; esac - exit ;; + ;; ppc64:Linux:*:*) - echo powerpc64-unknown-linux-gnu - exit ;; + GUESS=powerpc64-unknown-linux-$LIBC + ;; ppc:Linux:*:*) - echo powerpc-unknown-linux-gnu - exit ;; + GUESS=powerpc-unknown-linux-$LIBC + ;; + ppc64le:Linux:*:*) + GUESS=powerpc64le-unknown-linux-$LIBC + ;; + ppcle:Linux:*:*) + GUESS=powerpcle-unknown-linux-$LIBC + ;; riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux - exit ;; + GUESS=$UNAME_MACHINE-ibm-linux-$LIBC + ;; sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; tile*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; vax:Linux:*:*) - echo ${UNAME_MACHINE}-dec-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-dec-linux-$LIBC + ;; x86_64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + set_cc_for_build + CPU=$UNAME_MACHINE + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __i386__ + ABI=x86 + #else + #ifdef __ILP32__ + ABI=x32 + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + x86) CPU=i686 ;; + x32) LIBCABI=${LIBC}x32 ;; + esac + fi + GUESS=$CPU-pc-linux-$LIBCABI + ;; xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. - echo i386-sequent-sysv4 - exit ;; + GUESS=i386-sequent-sysv4 + ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. - echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} - exit ;; + GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION + ;; i*86:OS/2:*:*) - # If we were able to find `uname', then EMX Unix compatibility + # If we were able to find 'uname', then EMX Unix compatibility # is probably installed. - echo ${UNAME_MACHINE}-pc-os2-emx - exit ;; + GUESS=$UNAME_MACHINE-pc-os2-emx + ;; i*86:XTS-300:*:STOP) - echo ${UNAME_MACHINE}-unknown-stop - exit ;; + GUESS=$UNAME_MACHINE-unknown-stop + ;; i*86:atheos:*:*) - echo ${UNAME_MACHINE}-unknown-atheos - exit ;; + GUESS=$UNAME_MACHINE-unknown-atheos + ;; i*86:syllable:*:*) - echo ${UNAME_MACHINE}-pc-syllable - exit ;; + GUESS=$UNAME_MACHINE-pc-syllable + ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) - echo i386-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=i386-unknown-lynxos$UNAME_RELEASE + ;; i*86:*DOS:*:*) - echo ${UNAME_MACHINE}-pc-msdosdjgpp - exit ;; - i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) - UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + GUESS=$UNAME_MACHINE-pc-msdosdjgpp + ;; + i*86:*:4.*:*) + UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL fi - exit ;; + ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in @@ -1055,12 +1268,12 @@ EOF *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac - echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} - exit ;; + GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 @@ -1070,43 +1283,43 @@ EOF && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 - echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL else - echo ${UNAME_MACHINE}-pc-sysv32 + GUESS=$UNAME_MACHINE-pc-sysv32 fi - exit ;; + ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configury will decide that + # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. - echo i586-pc-msdosdjgpp - exit ;; + GUESS=i586-pc-msdosdjgpp + ;; Intel:Mach:3*:*) - echo i386-pc-mach3 - exit ;; + GUESS=i386-pc-mach3 + ;; paragon:*:*:*) - echo i860-intel-osf1 - exit ;; + GUESS=i860-intel-osf1 + ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 fi - exit ;; + ;; mini*:CTIX:SYS*5:*) # "miniframe" - echo m68010-convergent-sysv - exit ;; + GUESS=m68010-convergent-sysv + ;; mc68k:UNIX:SYSTEM5:3.51m) - echo m68k-convergent-sysv - exit ;; + GUESS=m68k-convergent-sysv + ;; M680?0:D-NIX:5.3:*) - echo m68k-diab-dnix - exit ;; + GUESS=m68k-diab-dnix + ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) @@ -1114,9 +1327,9 @@ EOF test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; @@ -1125,227 +1338,292 @@ EOF test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=m68k-unknown-lynxos$UNAME_RELEASE + ;; mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit ;; + GUESS=m68k-atari-sysv4 + ;; TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=sparc-unknown-lynxos$UNAME_RELEASE + ;; rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=rs6000-unknown-lynxos$UNAME_RELEASE + ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) - echo powerpc-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=powerpc-unknown-lynxos$UNAME_RELEASE + ;; SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv${UNAME_RELEASE} - exit ;; + GUESS=mips-dde-sysv$UNAME_RELEASE + ;; RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit ;; + GUESS=mips-sni-sysv4 + ;; RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit ;; + GUESS=mips-sni-sysv4 + ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo ${UNAME_MACHINE}-sni-sysv4 + GUESS=$UNAME_MACHINE-sni-sysv4 else - echo ns32k-sni-sysv + GUESS=ns32k-sni-sysv fi - exit ;; - PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + ;; + PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort # says - echo i586-unisys-sysv4 - exit ;; + GUESS=i586-unisys-sysv4 + ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit ;; + GUESS=hppa1.1-stratus-sysv4 + ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit ;; + GUESS=i860-stratus-sysv4 + ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. - echo ${UNAME_MACHINE}-stratus-vos - exit ;; + GUESS=$UNAME_MACHINE-stratus-vos + ;; *:VOS:*:*) # From Paul.Green@stratus.com. - echo hppa1.1-stratus-vos - exit ;; + GUESS=hppa1.1-stratus-vos + ;; mc68*:A/UX:*:*) - echo m68k-apple-aux${UNAME_RELEASE} - exit ;; + GUESS=m68k-apple-aux$UNAME_RELEASE + ;; news*:NEWS-OS:6*:*) - echo mips-sony-newsos6 - exit ;; + GUESS=mips-sony-newsos6 + ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then - echo mips-nec-sysv${UNAME_RELEASE} + if test -d /usr/nec; then + GUESS=mips-nec-sysv$UNAME_RELEASE else - echo mips-unknown-sysv${UNAME_RELEASE} + GUESS=mips-unknown-sysv$UNAME_RELEASE fi - exit ;; + ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit ;; + GUESS=powerpc-be-beos + ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit ;; + GUESS=powerpc-apple-beos + ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit ;; + GUESS=i586-pc-beos + ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. - echo i586-pc-haiku - exit ;; + GUESS=i586-pc-haiku + ;; + ppc:Haiku:*:*) # Haiku running on Apple PowerPC + GUESS=powerpc-apple-haiku + ;; + *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat) + GUESS=$UNAME_MACHINE-unknown-haiku + ;; SX-4:SUPER-UX:*:*) - echo sx4-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx4-nec-superux$UNAME_RELEASE + ;; SX-5:SUPER-UX:*:*) - echo sx5-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx5-nec-superux$UNAME_RELEASE + ;; SX-6:SUPER-UX:*:*) - echo sx6-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx6-nec-superux$UNAME_RELEASE + ;; SX-7:SUPER-UX:*:*) - echo sx7-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx7-nec-superux$UNAME_RELEASE + ;; SX-8:SUPER-UX:*:*) - echo sx8-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx8-nec-superux$UNAME_RELEASE + ;; SX-8R:SUPER-UX:*:*) - echo sx8r-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx8r-nec-superux$UNAME_RELEASE + ;; + SX-ACE:SUPER-UX:*:*) + GUESS=sxace-nec-superux$UNAME_RELEASE + ;; Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody${UNAME_RELEASE} - exit ;; + GUESS=powerpc-apple-rhapsody$UNAME_RELEASE + ;; *:Rhapsody:*:*) - echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE + ;; + arm64:Darwin:*:*) + GUESS=aarch64-apple-darwin$UNAME_RELEASE + ;; *:Darwin:*:*) - UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + UNAME_PROCESSOR=`uname -p` case $UNAME_PROCESSOR in - i386) - eval $set_cc_for_build - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - UNAME_PROCESSOR="x86_64" - fi - fi ;; unknown) UNAME_PROCESSOR=powerpc ;; esac - echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} - exit ;; + if command -v xcode-select > /dev/null 2> /dev/null && \ + ! xcode-select --print-path > /dev/null 2> /dev/null ; then + # Avoid executing cc if there is no toolchain installed as + # cc will be a stub that puts up a graphical alert + # prompting the user to install developer tools. + CC_FOR_BUILD=no_compiler_found + else + set_cc_for_build + fi + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # uname -m returns i386 or x86_64 + UNAME_PROCESSOR=$UNAME_MACHINE + fi + GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE + ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = "x86"; then + if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi - echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE + ;; *:QNX:*:4*) - echo i386-pc-qnx - exit ;; - NEO-?:NONSTOP_KERNEL:*:*) - echo neo-tandem-nsk${UNAME_RELEASE} - exit ;; + GUESS=i386-pc-qnx + ;; + NEO-*:NONSTOP_KERNEL:*:*) + GUESS=neo-tandem-nsk$UNAME_RELEASE + ;; NSE-*:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk${UNAME_RELEASE} - exit ;; - NSR-?:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk${UNAME_RELEASE} - exit ;; + GUESS=nse-tandem-nsk$UNAME_RELEASE + ;; + NSR-*:NONSTOP_KERNEL:*:*) + GUESS=nsr-tandem-nsk$UNAME_RELEASE + ;; + NSV-*:NONSTOP_KERNEL:*:*) + GUESS=nsv-tandem-nsk$UNAME_RELEASE + ;; + NSX-*:NONSTOP_KERNEL:*:*) + GUESS=nsx-tandem-nsk$UNAME_RELEASE + ;; *:NonStop-UX:*:*) - echo mips-compaq-nonstopux - exit ;; + GUESS=mips-compaq-nonstopux + ;; BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit ;; + GUESS=bs2000-siemens-sysv + ;; DS/*:UNIX_System_V:*:*) - echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE + ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. - if test "$cputype" = "386"; then + if test "${cputype-}" = 386; then UNAME_MACHINE=i386 - else - UNAME_MACHINE="$cputype" + elif test "x${cputype-}" != x; then + UNAME_MACHINE=$cputype fi - echo ${UNAME_MACHINE}-unknown-plan9 - exit ;; + GUESS=$UNAME_MACHINE-unknown-plan9 + ;; *:TOPS-10:*:*) - echo pdp10-unknown-tops10 - exit ;; + GUESS=pdp10-unknown-tops10 + ;; *:TENEX:*:*) - echo pdp10-unknown-tenex - exit ;; + GUESS=pdp10-unknown-tenex + ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) - echo pdp10-dec-tops20 - exit ;; + GUESS=pdp10-dec-tops20 + ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) - echo pdp10-xkl-tops20 - exit ;; + GUESS=pdp10-xkl-tops20 + ;; *:TOPS-20:*:*) - echo pdp10-unknown-tops20 - exit ;; + GUESS=pdp10-unknown-tops20 + ;; *:ITS:*:*) - echo pdp10-unknown-its - exit ;; + GUESS=pdp10-unknown-its + ;; SEI:*:*:SEIUX) - echo mips-sei-seiux${UNAME_RELEASE} - exit ;; + GUESS=mips-sei-seiux$UNAME_RELEASE + ;; *:DragonFly:*:*) - echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` - exit ;; + DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL + ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` - case "${UNAME_MACHINE}" in - A*) echo alpha-dec-vms ; exit ;; - I*) echo ia64-dec-vms ; exit ;; - V*) echo vax-dec-vms ; exit ;; + case $UNAME_MACHINE in + A*) GUESS=alpha-dec-vms ;; + I*) GUESS=ia64-dec-vms ;; + V*) GUESS=vax-dec-vms ;; esac ;; *:XENIX:*:SysV) - echo i386-pc-xenix - exit ;; + GUESS=i386-pc-xenix + ;; i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' - exit ;; + SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` + GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL + ;; i*86:rdos:*:*) - echo ${UNAME_MACHINE}-pc-rdos - exit ;; - i*86:AROS:*:*) - echo ${UNAME_MACHINE}-pc-aros - exit ;; + GUESS=$UNAME_MACHINE-pc-rdos + ;; + i*86:Fiwix:*:*) + GUESS=$UNAME_MACHINE-pc-fiwix + ;; + *:AROS:*:*) + GUESS=$UNAME_MACHINE-unknown-aros + ;; x86_64:VMkernel:*:*) - echo ${UNAME_MACHINE}-unknown-esx - exit ;; + GUESS=$UNAME_MACHINE-unknown-esx + ;; + amd64:Isilon\ OneFS:*:*) + GUESS=x86_64-unknown-onefs + ;; + *:Unleashed:*:*) + GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE + ;; + *:Ironclad:*:*) + GUESS=$UNAME_MACHINE-unknown-ironclad + ;; esac -#echo '(No uname command or uname output not recognized.)' 1>&2 -#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 +# Do we have a guess based on uname results? +if test "x$GUESS" != x; then + echo "$GUESS" + exit +fi -eval $set_cc_for_build -cat >$dummy.c < "$dummy.c" < -# include +#include +#include +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#include +#if defined(_SIZE_T_) || defined(SIGLOST) +#include +#endif +#endif #endif +int main () { #if defined (sony) @@ -1357,22 +1635,14 @@ main () #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 - "4" + "4" #else - "" + "" #endif - ); exit (0); + ); exit (0); #endif #endif -#if defined (__arm) && defined (__acorn) && defined (__unix) - printf ("arm-acorn-riscix\n"); exit (0); -#endif - -#if defined (hp300) && !defined (hpux) - printf ("m68k-hp-bsd\n"); exit (0); -#endif - #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" @@ -1412,39 +1682,54 @@ main () #endif #if defined (_SEQUENT_) - struct utsname un; - - uname(&un); - - if (strncmp(un.version, "V2", 2) == 0) { - printf ("i386-sequent-ptx2\n"); exit (0); - } - if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ - printf ("i386-sequent-ptx1\n"); exit (0); - } - printf ("i386-sequent-ptx\n"); exit (0); + struct utsname un; + uname(&un); + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) -# if !defined (ultrix) -# include -# if defined (BSD) -# if BSD == 43 - printf ("vax-dec-bsd4.3\n"); exit (0); -# else -# if BSD == 199006 - printf ("vax-dec-bsd4.3reno\n"); exit (0); -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# endif -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# else - printf ("vax-dec-ultrix\n"); exit (0); -# endif +#if !defined (ultrix) +#include +#if defined (BSD) +#if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +#else +#if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#endif +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#else +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname un; + uname (&un); + printf ("vax-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname *un; + uname (&un); + printf ("mips-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("mips-dec-ultrix\n"); exit (0); +#endif +#endif #endif #if defined (alliant) && defined (i860) @@ -1455,54 +1740,46 @@ main () } EOF -$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && +$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. +test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } -test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } +echo "$0: unable to guess system type" >&2 -# Convex versions that predate uname can use getsysinfo(1) +case $UNAME_MACHINE:$UNAME_SYSTEM in + mips:Linux | mips64:Linux) + # If we got here on MIPS GNU/Linux, output extra information. + cat >&2 <&2 < in order to provide the needed -information to handle your system. +our_year=`echo $timestamp | sed 's,-.*,,'` +thisyear=`date +%Y` +# shellcheck disable=SC2003 +script_age=`expr "$thisyear" - "$our_year"` +if test "$script_age" -lt 3 ; then + cat >&2 </dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` -UNAME_MACHINE = ${UNAME_MACHINE} -UNAME_RELEASE = ${UNAME_RELEASE} -UNAME_SYSTEM = ${UNAME_SYSTEM} -UNAME_VERSION = ${UNAME_VERSION} +UNAME_MACHINE = "$UNAME_MACHINE" +UNAME_RELEASE = "$UNAME_RELEASE" +UNAME_SYSTEM = "$UNAME_SYSTEM" +UNAME_VERSION = "$UNAME_VERSION" EOF +fi exit 1 # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" diff --git a/config.sub b/config.sub index b4c73e19d8..4aaae46f6f 100755 --- a/config.sub +++ b/config.sub @@ -1,36 +1,33 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -# 2011, 2012 Free Software Foundation, Inc. +# Copyright 1992-2024 Free Software Foundation, Inc. -timestamp='2012-06-17' +# shellcheck disable=SC2006,SC2268,SC2162 # see below for rationale -# This file is (in principle) common to ALL GNU software. -# The presence of a machine in this file suggests that SOME GNU software -# can handle that machine. It does not imply ALL GNU software can. -# -# This file is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or +timestamp='2024-05-27' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, see . +# along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). -# Please send patches to . Submit a context -# diff and a properly formatted GNU ChangeLog entry. +# Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. @@ -38,7 +35,7 @@ timestamp='2012-06-17' # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD +# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases @@ -55,15 +52,21 @@ timestamp='2012-06-17' # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + me=`echo "$0" | sed -e 's,.*/,,'` usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS - $0 [OPTION] ALIAS +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. -Operation modes: +Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit @@ -73,15 +76,13 @@ Report bugs and patches to ." version="\ GNU config.sub ($timestamp) -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, -2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 -Free Software Foundation, Inc. +Copyright 1992-2024 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" -Try \`$me --help' for more information." +Try '$me --help' for more information." # Parse command line while test $# -gt 0 ; do @@ -97,12 +98,12 @@ while test $# -gt 0 ; do - ) # Use stdin as input. break ;; -* ) - echo "$me: invalid option $1$help" + echo "$me: invalid option $1$help" >&2 exit 1 ;; *local*) # First pass through any local machine types. - echo $1 + echo "$1" exit ;; * ) @@ -118,1402 +119,1619 @@ case $# in exit 1;; esac -# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). -# Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` -case $maybe_os in - nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ - linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ - knetbsd*-gnu* | netbsd*-gnu* | \ - kopensolaris*-gnu* | \ - storm-chaos* | os2-emx* | rtmk-nova*) - os=-$maybe_os - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` - ;; - android-linux) - os=-linux-android - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown - ;; - *) - basic_machine=`echo $1 | sed 's/-[^-]*$//'` - if [ $basic_machine != $1 ] - then os=`echo $1 | sed 's/.*-/-/'` - else os=; fi - ;; -esac +# Split fields of configuration type +saved_IFS=$IFS +IFS="-" read field1 field2 field3 field4 <&2 + exit 1 ;; - -ptx*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + *-*-*-*) + basic_machine=$field1-$field2 + basic_os=$field3-$field4 ;; - -windowsnt*) - os=`echo $os | sed -e 's/windowsnt/winnt/'` + *-*-*) + # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two + # parts + maybe_os=$field2-$field3 + case $maybe_os in + cloudabi*-eabi* \ + | kfreebsd*-gnu* \ + | knetbsd*-gnu* \ + | kopensolaris*-gnu* \ + | linux-* \ + | managarm-* \ + | netbsd*-eabi* \ + | netbsd*-gnu* \ + | nto-qnx* \ + | os2-emx* \ + | rtmk-nova* \ + | storm-chaos* \ + | uclinux-gnu* \ + | uclinux-uclibc* \ + | windows-* ) + basic_machine=$field1 + basic_os=$maybe_os + ;; + android-linux) + basic_machine=$field1-unknown + basic_os=linux-android + ;; + *) + basic_machine=$field1-$field2 + basic_os=$field3 + ;; + esac ;; - -psos*) - os=-psos + *-*) + case $field1-$field2 in + # Shorthands that happen to contain a single dash + convex-c[12] | convex-c3[248]) + basic_machine=$field2-convex + basic_os= + ;; + decstation-3100) + basic_machine=mips-dec + basic_os= + ;; + *-*) + # Second component is usually, but not always the OS + case $field2 in + # Do not treat sunos as a manufacturer + sun*os*) + basic_machine=$field1 + basic_os=$field2 + ;; + # Manufacturers + 3100* \ + | 32* \ + | 3300* \ + | 3600* \ + | 7300* \ + | acorn \ + | altos* \ + | apollo \ + | apple \ + | atari \ + | att* \ + | axis \ + | be \ + | bull \ + | cbm \ + | ccur \ + | cisco \ + | commodore \ + | convergent* \ + | convex* \ + | cray \ + | crds \ + | dec* \ + | delta* \ + | dg \ + | digital \ + | dolphin \ + | encore* \ + | gould \ + | harris \ + | highlevel \ + | hitachi* \ + | hp \ + | ibm* \ + | intergraph \ + | isi* \ + | knuth \ + | masscomp \ + | microblaze* \ + | mips* \ + | motorola* \ + | ncr* \ + | news \ + | next \ + | ns \ + | oki \ + | omron* \ + | pc533* \ + | rebel \ + | rom68k \ + | rombug \ + | semi \ + | sequent* \ + | siemens \ + | sgi* \ + | siemens \ + | sim \ + | sni \ + | sony* \ + | stratus \ + | sun \ + | sun[234]* \ + | tektronix \ + | tti* \ + | ultra \ + | unicom* \ + | wec \ + | winbond \ + | wrs) + basic_machine=$field1-$field2 + basic_os= + ;; + zephyr*) + basic_machine=$field1-unknown + basic_os=$field2 + ;; + *) + basic_machine=$field1 + basic_os=$field2 + ;; + esac + ;; + esac ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint + *) + # Convert single-component short-hands not valid as part of + # multi-component configurations. + case $field1 in + 386bsd) + basic_machine=i386-pc + basic_os=bsd + ;; + a29khif) + basic_machine=a29k-amd + basic_os=udi + ;; + adobe68k) + basic_machine=m68010-adobe + basic_os=scout + ;; + alliant) + basic_machine=fx80-alliant + basic_os= + ;; + altos | altos3068) + basic_machine=m68k-altos + basic_os= + ;; + am29k) + basic_machine=a29k-none + basic_os=bsd + ;; + amdahl) + basic_machine=580-amdahl + basic_os=sysv + ;; + amiga) + basic_machine=m68k-unknown + basic_os= + ;; + amigaos | amigados) + basic_machine=m68k-unknown + basic_os=amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + basic_os=sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + basic_os=sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + basic_os=bsd + ;; + aros) + basic_machine=i386-pc + basic_os=aros + ;; + aux) + basic_machine=m68k-apple + basic_os=aux + ;; + balance) + basic_machine=ns32k-sequent + basic_os=dynix + ;; + blackfin) + basic_machine=bfin-unknown + basic_os=linux + ;; + cegcc) + basic_machine=arm-unknown + basic_os=cegcc + ;; + cray) + basic_machine=j90-cray + basic_os=unicos + ;; + crds | unos) + basic_machine=m68k-crds + basic_os= + ;; + da30) + basic_machine=m68k-da30 + basic_os= + ;; + decstation | pmax | pmin | dec3100 | decstatn) + basic_machine=mips-dec + basic_os= + ;; + delta88) + basic_machine=m88k-motorola + basic_os=sysv3 + ;; + dicos) + basic_machine=i686-pc + basic_os=dicos + ;; + djgpp) + basic_machine=i586-pc + basic_os=msdosdjgpp + ;; + ebmon29k) + basic_machine=a29k-amd + basic_os=ebmon + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + basic_os=ose + ;; + gmicro) + basic_machine=tron-gmicro + basic_os=sysv + ;; + go32) + basic_machine=i386-pc + basic_os=go32 + ;; + h8300hms) + basic_machine=h8300-hitachi + basic_os=hms + ;; + h8300xray) + basic_machine=h8300-hitachi + basic_os=xray + ;; + h8500hms) + basic_machine=h8500-hitachi + basic_os=hms + ;; + harris) + basic_machine=m88k-harris + basic_os=sysv3 + ;; + hp300 | hp300hpux) + basic_machine=m68k-hp + basic_os=hpux + ;; + hp300bsd) + basic_machine=m68k-hp + basic_os=bsd + ;; + hppaosf) + basic_machine=hppa1.1-hp + basic_os=osf + ;; + hppro) + basic_machine=hppa1.1-hp + basic_os=proelf + ;; + i386mach) + basic_machine=i386-mach + basic_os=mach + ;; + isi68 | isi) + basic_machine=m68k-isi + basic_os=sysv + ;; + m68knommu) + basic_machine=m68k-unknown + basic_os=linux + ;; + magnum | m3230) + basic_machine=mips-mips + basic_os=sysv + ;; + merlin) + basic_machine=ns32k-utek + basic_os=sysv + ;; + mingw64) + basic_machine=x86_64-pc + basic_os=mingw64 + ;; + mingw32) + basic_machine=i686-pc + basic_os=mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + basic_os=mingw32ce + ;; + monitor) + basic_machine=m68k-rom68k + basic_os=coff + ;; + morphos) + basic_machine=powerpc-unknown + basic_os=morphos + ;; + moxiebox) + basic_machine=moxie-unknown + basic_os=moxiebox + ;; + msdos) + basic_machine=i386-pc + basic_os=msdos + ;; + msys) + basic_machine=i686-pc + basic_os=msys + ;; + mvs) + basic_machine=i370-ibm + basic_os=mvs + ;; + nacl) + basic_machine=le32-unknown + basic_os=nacl + ;; + ncr3000) + basic_machine=i486-ncr + basic_os=sysv4 + ;; + netbsd386) + basic_machine=i386-pc + basic_os=netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + basic_os=linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + basic_os=newsos + ;; + news1000) + basic_machine=m68030-sony + basic_os=newsos + ;; + necv70) + basic_machine=v70-nec + basic_os=sysv + ;; + nh3000) + basic_machine=m68k-harris + basic_os=cxux + ;; + nh[45]000) + basic_machine=m88k-harris + basic_os=cxux + ;; + nindy960) + basic_machine=i960-intel + basic_os=nindy + ;; + mon960) + basic_machine=i960-intel + basic_os=mon960 + ;; + nonstopux) + basic_machine=mips-compaq + basic_os=nonstopux + ;; + os400) + basic_machine=powerpc-ibm + basic_os=os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + basic_os=ose + ;; + os68k) + basic_machine=m68k-none + basic_os=os68k + ;; + paragon) + basic_machine=i860-intel + basic_os=osf + ;; + parisc) + basic_machine=hppa-unknown + basic_os=linux + ;; + psp) + basic_machine=mipsallegrexel-sony + basic_os=psp + ;; + pw32) + basic_machine=i586-unknown + basic_os=pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + basic_os=rdos + ;; + rdos32) + basic_machine=i386-pc + basic_os=rdos + ;; + rom68k) + basic_machine=m68k-rom68k + basic_os=coff + ;; + sa29200) + basic_machine=a29k-amd + basic_os=udi + ;; + sei) + basic_machine=mips-sei + basic_os=seiux + ;; + sequent) + basic_machine=i386-sequent + basic_os= + ;; + sps7) + basic_machine=m68k-bull + basic_os=sysv2 + ;; + st2000) + basic_machine=m68k-tandem + basic_os= + ;; + stratus) + basic_machine=i860-stratus + basic_os=sysv4 + ;; + sun2) + basic_machine=m68000-sun + basic_os= + ;; + sun2os3) + basic_machine=m68000-sun + basic_os=sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + basic_os=sunos4 + ;; + sun3) + basic_machine=m68k-sun + basic_os= + ;; + sun3os3) + basic_machine=m68k-sun + basic_os=sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + basic_os=sunos4 + ;; + sun4) + basic_machine=sparc-sun + basic_os= + ;; + sun4os3) + basic_machine=sparc-sun + basic_os=sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + basic_os=sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + basic_os=solaris2 + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + basic_os= + ;; + sv1) + basic_machine=sv1-cray + basic_os=unicos + ;; + symmetry) + basic_machine=i386-sequent + basic_os=dynix + ;; + t3e) + basic_machine=alphaev5-cray + basic_os=unicos + ;; + t90) + basic_machine=t90-cray + basic_os=unicos + ;; + toad1) + basic_machine=pdp10-xkl + basic_os=tops20 + ;; + tpf) + basic_machine=s390x-ibm + basic_os=tpf + ;; + udi29k) + basic_machine=a29k-amd + basic_os=udi + ;; + ultra3) + basic_machine=a29k-nyu + basic_os=sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + basic_os=none + ;; + vaxv) + basic_machine=vax-dec + basic_os=sysv + ;; + vms) + basic_machine=vax-dec + basic_os=vms + ;; + vsta) + basic_machine=i386-pc + basic_os=vsta + ;; + vxworks960) + basic_machine=i960-wrs + basic_os=vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + basic_os=vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + basic_os=vxworks + ;; + xbox) + basic_machine=i686-pc + basic_os=mingw32 + ;; + ymp) + basic_machine=ymp-cray + basic_os=unicos + ;; + *) + basic_machine=$1 + basic_os= + ;; + esac ;; esac -# Decode aliases for certain CPU-COMPANY combinations. +# Decode 1-component or ad-hoc basic machines case $basic_machine in - # Recognize the basic CPU types without company name. - # Some are omitted here because they have special meanings below. - 1750a | 580 \ - | a29k \ - | aarch64 | aarch64_be \ - | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ - | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ - | am33_2.0 \ - | arc | arm | arm64 | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | armv[67] | armv7s | avr | avr32 \ - | be32 | be64 \ - | bfin \ - | c4x | clipper \ - | d10v | d30v | dlx | dsp16xx \ - | epiphany \ - | fido | fr30 | frv \ - | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ - | hexagon \ - | i370 | i860 | i960 | ia64 \ - | ip2k | iq2000 \ - | le32 | le64 \ - | lm32 \ - | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | mcore | mep | metag \ - | mips | mipsbe | mipseb | mipsel | mipsle \ - | mips16 \ - | mips64 | mips64el \ - | mips64octeon | mips64octeonel \ - | mips64orion | mips64orionel \ - | mips64r5900 | mips64r5900el \ - | mips64vr | mips64vrel \ - | mips64vr4100 | mips64vr4100el \ - | mips64vr4300 | mips64vr4300el \ - | mips64vr5000 | mips64vr5000el \ - | mips64vr5900 | mips64vr5900el \ - | mipsisa32 | mipsisa32el \ - | mipsisa32r2 | mipsisa32r2el \ - | mipsisa64 | mipsisa64el \ - | mipsisa64r2 | mipsisa64r2el \ - | mipsisa64sb1 | mipsisa64sb1el \ - | mipsisa64sr71k | mipsisa64sr71kel \ - | mipstx39 | mipstx39el \ - | mn10200 | mn10300 \ - | moxie \ - | mt \ - | msp430 \ - | nds32 | nds32le | nds32be \ - | nios | nios2 \ - | ns16k | ns32k \ - | open8 \ - | or32 \ - | pdp10 | pdp11 | pj | pjl \ - | powerpc | powerpc64 | powerpc64le | powerpcle \ - | pyramid \ - | riscv | riscv32 | riscv32be | riscv64 | riscv64be \ - | rl78 | rx \ - | score \ - | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ - | sh64 | sh64le \ - | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ - | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ - | spu \ - | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ - | ubicom32 \ - | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ - | we32k \ - | x86 | xc16x | xstormy16 | xtensa \ - | z8k | z80) - basic_machine=$basic_machine-unknown - ;; - c54x) - basic_machine=tic54x-unknown - ;; - c55x) - basic_machine=tic55x-unknown - ;; - c6x) - basic_machine=tic6x-unknown - ;; - m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip) - basic_machine=$basic_machine-unknown - os=-none - ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) - ;; - ms1) - basic_machine=mt-unknown + # Here we handle the default manufacturer of certain CPU types. It is in + # some cases the only manufacturer, in others, it is the most popular. + w89k) + cpu=hppa1.1 + vendor=winbond ;; - - strongarm | thumb | xscale) - basic_machine=arm-unknown + op50n) + cpu=hppa1.1 + vendor=oki ;; - xgate) - basic_machine=$basic_machine-unknown - os=-none + op60c) + cpu=hppa1.1 + vendor=oki ;; - xscaleeb) - basic_machine=armeb-unknown + ibm*) + cpu=i370 + vendor=ibm ;; - - xscaleel) - basic_machine=armel-unknown + orion105) + cpu=clipper + vendor=highlevel ;; - - # We use `pc' rather than `unknown' - # because (1) that's what they normally are, and - # (2) the word "unknown" tends to confuse beginning users. - i*86 | x86_64) - basic_machine=$basic_machine-pc - ;; - # Object if more than one company name word. - *-*-*) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 + mac | mpw | mac-mpw) + cpu=m68k + vendor=apple ;; - # Recognize the basic CPU types with company name. - 580-* \ - | a29k-* \ - | aarch64-* | aarch64_be-* \ - | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ - | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ - | arm-* | arm64-* | armbe-* | armle-* | armeb-* | armv*-* \ - | avr-* | avr32-* \ - | be32-* | be64-* \ - | bfin-* | bs2000-* \ - | c[123]* | c30-* | [cjt]90-* | c4x-* \ - | clipper-* | craynv-* | cydra-* \ - | d10v-* | d30v-* | dlx-* \ - | elxsi-* \ - | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ - | h8300-* | h8500-* \ - | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ - | hexagon-* \ - | i*86-* | i860-* | i960-* | ia64-* \ - | ip2k-* | iq2000-* \ - | le32-* | le64-* \ - | lm32-* \ - | m32c-* | m32r-* | m32rle-* \ - | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ - | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ - | mips16-* \ - | mips64-* | mips64el-* \ - | mips64octeon-* | mips64octeonel-* \ - | mips64orion-* | mips64orionel-* \ - | mips64r5900-* | mips64r5900el-* \ - | mips64vr-* | mips64vrel-* \ - | mips64vr4100-* | mips64vr4100el-* \ - | mips64vr4300-* | mips64vr4300el-* \ - | mips64vr5000-* | mips64vr5000el-* \ - | mips64vr5900-* | mips64vr5900el-* \ - | mipsisa32-* | mipsisa32el-* \ - | mipsisa32r2-* | mipsisa32r2el-* \ - | mipsisa64-* | mipsisa64el-* \ - | mipsisa64r2-* | mipsisa64r2el-* \ - | mipsisa64sb1-* | mipsisa64sb1el-* \ - | mipsisa64sr71k-* | mipsisa64sr71kel-* \ - | mipstx39-* | mipstx39el-* \ - | mmix-* \ - | mt-* \ - | msp430-* \ - | nds32-* | nds32le-* | nds32be-* \ - | nios-* | nios2-* \ - | none-* | np1-* | ns16k-* | ns32k-* \ - | open8-* \ - | orion-* \ - | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ - | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ - | pyramid-* \ - | riscv-* | riscv32-* | riscv32be-* | riscv64-* | riscv64be-* \ - | rl78-* | romp-* | rs6000-* | rx-* \ - | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ - | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ - | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ - | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ - | tahoe-* \ - | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ - | tile*-* \ - | tron-* \ - | ubicom32-* \ - | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ - | vax-* \ - | we32k-* \ - | x86-* | x86_64-* | xc16x-* | xps100-* \ - | xstormy16-* | xtensa*-* \ - | ymp-* \ - | z8k-* | z80-*) - ;; - # Recognize the basic CPU types without company name, with glob match. - xtensa*) - basic_machine=$basic_machine-unknown + pmac | pmac-mpw) + cpu=powerpc + vendor=apple ;; + # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-unknown - os=-bsd - ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - basic_machine=m68000-att + cpu=m68000 + vendor=att ;; 3b*) - basic_machine=we32k-att - ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; - abacus) - basic_machine=abacus-unknown - ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; - alliant | fx80) - basic_machine=fx80-alliant - ;; - altos | altos3068) - basic_machine=m68k-altos - ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; - amd64) - basic_machine=x86_64-pc - ;; - amd64-*) - basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv - ;; - amiga | amiga-*) - basic_machine=m68k-unknown - ;; - amigaos | amigados) - basic_machine=m68k-unknown - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aros) - basic_machine=i386-pc - os=-aros - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - blackfin) - basic_machine=bfin-unknown - os=-linux - ;; - blackfin-*) - basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux + cpu=we32k + vendor=att ;; bluegene*) - basic_machine=powerpc-ibm - os=-cnk - ;; - c54x-*) - basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c55x-*) - basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c6x-*) - basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c90) - basic_machine=c90-cray - os=-unicos - ;; - cegcc) - basic_machine=arm-unknown - os=-cegcc - ;; - convex-c1) - basic_machine=c1-convex - os=-bsd - ;; - convex-c2) - basic_machine=c2-convex - os=-bsd - ;; - convex-c32) - basic_machine=c32-convex - os=-bsd - ;; - convex-c34) - basic_machine=c34-convex - os=-bsd - ;; - convex-c38) - basic_machine=c38-convex - os=-bsd - ;; - cray | j90) - basic_machine=j90-cray - os=-unicos - ;; - craynv) - basic_machine=craynv-cray - os=-unicosmp - ;; - cr16 | cr16-*) - basic_machine=cr16-unknown - os=-elf - ;; - crds | unos) - basic_machine=m68k-crds - ;; - crisv32 | crisv32-* | etraxfs*) - basic_machine=crisv32-axis - ;; - cris | cris-* | etrax*) - basic_machine=cris-axis - ;; - crx) - basic_machine=crx-unknown - os=-elf - ;; - da30 | da30-*) - basic_machine=m68k-da30 - ;; - decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) - basic_machine=mips-dec + cpu=powerpc + vendor=ibm + basic_os=cnk ;; decsystem10* | dec10*) - basic_machine=pdp10-dec - os=-tops10 + cpu=pdp10 + vendor=dec + basic_os=tops10 ;; decsystem20* | dec20*) - basic_machine=pdp10-dec - os=-tops20 - ;; - delta | 3300 | motorola-3300 | motorola-delta \ - | 3300-motorola | delta-motorola) - basic_machine=m68k-motorola - ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - dicos) - basic_machine=i686-pc - os=-dicos - ;; - djgpp) - basic_machine=i586-pc - os=-msdosdjgpp - ;; - dpx20 | dpx20-*) - basic_machine=rs6000-bull - os=-bosx - ;; - dpx2* | dpx2*-bull) - basic_machine=m68k-bull - os=-sysv3 - ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon - ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd + cpu=pdp10 + vendor=dec + basic_os=tops20 + ;; + delta | 3300 | delta-motorola | 3300-motorola | motorola-delta | motorola-3300) + cpu=m68k + vendor=motorola + ;; + # This used to be dpx2*, but that gets the RS6000-based + # DPX/20 and the x86-based DPX/2-100 wrong. See + # https://oldskool.silicium.org/stations/bull_dpx20.htm + # https://www.feb-patrimoine.com/english/bull_dpx2.htm + # https://www.feb-patrimoine.com/english/unix_and_bull.htm + dpx2 | dpx2[23]00 | dpx2[23]xx) + cpu=m68k + vendor=bull + ;; + dpx2100 | dpx21xx) + cpu=i386 + vendor=bull + ;; + dpx20) + cpu=rs6000 + vendor=bull ;; encore | umax | mmax) - basic_machine=ns32k-encore + cpu=ns32k + vendor=encore ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose + elxsi) + cpu=elxsi + vendor=elxsi + basic_os=${basic_os:-bsd} ;; fx2800) - basic_machine=i860-alliant + cpu=i860 + vendor=alliant ;; genix) - basic_machine=ns32k-ns - ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - go32) - basic_machine=i386-pc - os=-go32 + cpu=ns32k + vendor=ns ;; h3050r* | hiux*) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 - ;; - hp300-*) - basic_machine=m68k-hp - ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) - basic_machine=hppa1.0-hp + cpu=hppa1.0 + vendor=hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) - basic_machine=m68000-hp + cpu=m68000 + vendor=hp ;; hp9k3[2-9][0-9]) - basic_machine=m68k-hp + cpu=m68k + vendor=hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) - basic_machine=hppa1.0-hp + cpu=hppa1.0 + vendor=hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hppa-next) - os=-nextstep3 - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; - i370-ibm* | ibm*) - basic_machine=i370-ibm + cpu=hppa1.0 + vendor=hp ;; i*86v32) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv32 + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv32 ;; i*86v4*) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv4 + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv4 ;; i*86v) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv ;; i*86sol2) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-solaris2 + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=solaris2 ;; - i386mach) - basic_machine=i386-mach - os=-mach - ;; - i386-vsta | vsta) - basic_machine=i386-unknown - os=-vsta + j90 | j90-cray) + cpu=j90 + vendor=cray + basic_os=${basic_os:-unicos} ;; iris | iris4d) - basic_machine=mips-sgi - case $os in - -irix*) + cpu=mips + vendor=sgi + case $basic_os in + irix*) ;; *) - os=-irix4 + basic_os=irix4 ;; esac ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - m68knommu) - basic_machine=m68k-unknown - os=-linux - ;; - m68knommu-*) - basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - m88k-omron*) - basic_machine=m88k-omron - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv - ;; - microblaze) - basic_machine=microblaze-xilinx - ;; - mingw32) - basic_machine=i386-pc - os=-mingw32 - ;; - mingw32ce) - basic_machine=arm-unknown - os=-mingw32ce - ;; miniframe) - basic_machine=m68000-convergent - ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; - mips3*-*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + cpu=m68000 + vendor=convergent ;; - mips3*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown - ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - morphos) - basic_machine=powerpc-unknown - os=-morphos - ;; - msdos) - basic_machine=i386-pc - os=-msdos - ;; - ms1-*) - basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` - ;; - msys) - basic_machine=i386-pc - os=-msys - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - nacl) - basic_machine=le32-unknown - os=-nacl - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos + *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) + cpu=m68k + vendor=atari + basic_os=mint ;; news-3600 | risc-news) - basic_machine=mips-sony - os=-newsos + cpu=mips + vendor=sony + basic_os=newsos ;; - necv70) - basic_machine=v70-nec - os=-sysv - ;; - next | m*-next ) - basic_machine=m68k-next - case $os in - -nextstep* ) - ;; - -ns2*) - os=-nextstep2 - ;; - *) - os=-nextstep3 - ;; - esac - ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - nonstopux) - basic_machine=mips-compaq - os=-nonstopux + next | m*-next) + cpu=m68k + vendor=next ;; np1) - basic_machine=np1-gould - ;; - neo-tandem) - basic_machine=neo-tandem - ;; - nse-tandem) - basic_machine=nse-tandem - ;; - nsr-tandem) - basic_machine=nsr-tandem + cpu=np1 + vendor=gould ;; op50n-* | op60c-*) - basic_machine=hppa1.1-oki - os=-proelf - ;; - openrisc | openrisc-*) - basic_machine=or32-unknown - ;; - os400) - basic_machine=powerpc-ibm - os=-os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k + cpu=hppa1.1 + vendor=oki + basic_os=proelf ;; pa-hitachi) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - parisc) - basic_machine=hppa-unknown - os=-linux - ;; - parisc-*) - basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 ;; pbd) - basic_machine=sparc-tti + cpu=sparc + vendor=tti ;; pbb) - basic_machine=m68k-tti + cpu=m68k + vendor=tti ;; - pc532 | pc532-*) - basic_machine=ns32k-pc532 + pc532) + cpu=ns32k + vendor=pc532 ;; - pc98) - basic_machine=i386-pc + pn) + cpu=pn + vendor=gould ;; - pc98-*) - basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + power) + cpu=power + vendor=ibm ;; - pentium | p5 | k5 | k6 | nexgen | viac3) - basic_machine=i586-pc + ps2) + cpu=i386 + vendor=ibm ;; - pentiumpro | p6 | 6x86 | athlon | athlon_*) - basic_machine=i686-pc + rm[46]00) + cpu=mips + vendor=siemens ;; - pentiumii | pentium2 | pentiumiii | pentium3) - basic_machine=i686-pc + rtpc | rtpc-*) + cpu=romp + vendor=ibm ;; - pentium4) - basic_machine=i786-pc + sde) + cpu=mipsisa32 + vendor=sde + basic_os=${basic_os:-elf} ;; - pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) - basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + simso-wrs) + cpu=sparclite + vendor=wrs + basic_os=vxworks ;; - pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + tower | tower-32) + cpu=m68k + vendor=ncr ;; - pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + vpp*|vx|vx-*) + cpu=f301 + vendor=fujitsu ;; - pentium4-*) - basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + w65) + cpu=w65 + vendor=wdc ;; - pn) - basic_machine=pn-gould + w89k-*) + cpu=hppa1.1 + vendor=winbond + basic_os=proelf ;; - power) basic_machine=power-ibm + none) + cpu=none + vendor=none ;; - ppc | ppcbe) basic_machine=powerpc-unknown + leon|leon[3-9]) + cpu=sparc + vendor=$basic_machine ;; - ppc-* | ppcbe-*) - basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + leon-*|leon[3-9]-*) + cpu=sparc + vendor=`echo "$basic_machine" | sed 's/-.*//'` ;; - ppcle | powerpclittle | ppc-le | powerpc-little) - basic_machine=powerpcle-unknown + + *-*) + saved_IFS=$IFS + IFS="-" read cpu vendor <&2 + exit 1 + ;; + esac ;; +esac -# Here we handle the default manufacturer of certain CPU types. It is in -# some cases the only manufacturer, in others, it is the most popular. - w89k) - basic_machine=hppa1.1-winbond +# Here we canonicalize certain aliases for manufacturers. +case $vendor in + digital*) + vendor=dec ;; - op50n) - basic_machine=hppa1.1-oki + commodore*) + vendor=cbm ;; - op60c) - basic_machine=hppa1.1-oki + *) ;; - romp) - basic_machine=romp-ibm +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if test x"$basic_os" != x +then + +# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just +# set os. +obj= +case $basic_os in + gnu/linux*) + kernel=linux + os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` + ;; + os2-emx) + kernel=os2 + os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` + ;; + nto-qnx*) + kernel=nto + os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` + ;; + *-*) + saved_IFS=$IFS + IFS="-" read kernel os <&2 - exit 1 + sco5) + os=sco3.2v5 ;; -esac - -# Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + sco4) + os=sco3.2v4 ;; - *-commodore*) - basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + sco3.2.[4-9]*) + os=`echo "$os" | sed -e 's/sco3.2./sco3.2v/'` ;; - *) + sco*v* | scout) + # Don't match below ;; -esac - -# Decode manufacturer-specific aliases for certain operating systems. - -if [ x"$os" != x"" ] -then -case $os in - # First match some system type aliases - # that might get confused with valid system types. - # -solaris* is a basic system type, with this one exception. - -auroraux) - os=-auroraux - ;; - -solaris1 | -solaris1.*) - os=`echo $os | sed -e 's|solaris1|sunos4|'` - ;; - -solaris) - os=-solaris2 - ;; - -svr4*) - os=-sysv4 - ;; - -unixware*) - os=-sysv4.2uw - ;; - -gnu/linux*) - os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` - ;; - # First accept the basic system types. - # The portable systems comes first. - # Each alternative MUST END IN A *, to match a version number. - # -sysv* is not here because it comes later, after sysvr4. - -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ - | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ - | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ - | -sym* | -kopensolaris* \ - | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* | -aros* \ - | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ - | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ - | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ - | -bitrig* | -openbsd* | -solidbsd* \ - | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ - | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ - | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ - | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ - | -chorusos* | -chorusrdb* | -cegcc* \ - | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -mingw32* | -linux-gnu* | -linux-android* \ - | -linux-newlib* | -linux-uclibc* \ - | -uxpv* | -beos* | -mpeix* | -udk* \ - | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ - | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ - | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ - | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ - | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ - | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) - # Remember, each alternative MUST END IN *, to match a version number. - ;; - -qnx*) - case $basic_machine in - x86-* | i*86-*) - ;; - *) - os=-nto$os - ;; - esac + sco*) + os=sco3.2v2 ;; - -nto-qnx*) + psos*) + os=psos ;; - -nto*) - os=`echo $os | sed -e 's|nto|nto-qnx|'` + qnx*) + os=qnx ;; - -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ - | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ - | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + hiux*) + os=hiuxwe2 ;; - -mac*) - os=`echo $os | sed -e 's|mac|macos|'` + lynx*178) + os=lynxos178 ;; - -linux-dietlibc) - os=-linux-dietlibc + lynx*5) + os=lynxos5 ;; - -linux*) - os=`echo $os | sed -e 's|linux|linux-gnu|'` + lynxos*) + # don't get caught up in next wildcard ;; - -sunos5*) - os=`echo $os | sed -e 's|sunos5|solaris2|'` + lynx*) + os=lynxos ;; - -sunos6*) - os=`echo $os | sed -e 's|sunos6|solaris3|'` + mac[0-9]*) + os=`echo "$os" | sed -e 's|mac|macos|'` ;; - -opened*) - os=-openedition + opened*) + os=openedition ;; - -os400*) - os=-os400 + os400*) + os=os400 ;; - -wince*) - os=-wince + sunos5*) + os=`echo "$os" | sed -e 's|sunos5|solaris2|'` ;; - -osfrose*) - os=-osfrose + sunos6*) + os=`echo "$os" | sed -e 's|sunos6|solaris3|'` ;; - -osf*) - os=-osf + wince*) + os=wince ;; - -utek*) - os=-bsd + utek*) + os=bsd + vendor=`echo "$vendor" | sed -e 's|^unknown$|tektronix|'` ;; - -dynix*) - os=-bsd + dynix*) + os=bsd ;; - -acis*) - os=-aos + acis*) + os=aos ;; - -atheos*) - os=-atheos + atheos*) + os=atheos ;; - -syllable*) - os=-syllable + syllable*) + os=syllable ;; - -386bsd) - os=-bsd - ;; - -ctix* | -uts*) - os=-sysv + 386bsd) + os=bsd ;; - -nova*) - os=-rtmk-nova + ctix*) + os=sysv + vendor=`echo "$vendor" | sed -e 's|^unknown$|convergent|'` ;; - -ns2 ) - os=-nextstep2 + uts*) + os=sysv ;; - -nsk*) - os=-nsk + nova*) + kernel=rtmk + os=nova ;; # Preserve the version number of sinix5. - -sinix5.*) - os=`echo $os | sed -e 's|sinix|sysv|'` - ;; - -sinix*) - os=-sysv4 - ;; - -tpf*) - os=-tpf - ;; - -triton*) - os=-sysv3 - ;; - -oss*) - os=-sysv3 + sinix5.*) + os=`echo "$os" | sed -e 's|sinix|sysv|'` + vendor=`echo "$vendor" | sed -e 's|^unknown$|sni|'` ;; - -svr4) - os=-sysv4 + sinix*) + os=sysv4 + vendor=`echo "$vendor" | sed -e 's|^unknown$|sni|'` ;; - -svr3) - os=-sysv3 + tpf*) + os=tpf ;; - -sysvr4) - os=-sysv4 + triton*) + os=sysv3 ;; - # This must come after -sysvr4. - -sysv*) + oss*) + os=sysv3 ;; - -ose*) - os=-ose + svr4*) + os=sysv4 ;; - -es1800*) - os=-ose + svr3) + os=sysv3 ;; - -xenix) - os=-xenix + sysvr4) + os=sysv4 ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - os=-mint + ose*) + os=ose ;; - -aros*) - os=-aros + *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) + os=mint ;; - -kaos*) - os=-kaos + dicos*) + os=dicos ;; - -zvmoe) - os=-zvmoe - ;; - -dicos*) - os=-dicos - ;; - -nacl*) + pikeos*) + # Until real need of OS specific support for + # particular features comes up, bare metal + # configurations are quite functional. + case $cpu in + arm*) + os=eabi + ;; + *) + os= + obj=elf + ;; + esac ;; - -none) + aout* | coff* | elf* | pe*) + # These are machine code file formats, not OSes + obj=$os + os= ;; *) - # Get rid of the `-' at the beginning of $os. - os=`echo $os | sed 's/[^-]*-//'` - echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 - exit 1 + # No normalization, but not necessarily accepted, that comes below. ;; esac + else # Here we handle the default operating systems that come with various machines. @@ -1526,258 +1744,610 @@ else # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. -case $basic_machine in +kernel= +obj= +case $cpu-$vendor in score-*) - os=-elf + os= + obj=elf ;; spu-*) - os=-elf + os= + obj=elf ;; *-acorn) - os=-riscix1.2 + os=riscix1.2 ;; arm*-rebel) - os=-linux + kernel=linux + os=gnu ;; arm*-semi) - os=-aout + os= + obj=aout ;; c4x-* | tic4x-*) - os=-coff + os= + obj=coff + ;; + c8051-*) + os= + obj=elf + ;; + clipper-intergraph) + os=clix ;; hexagon-*) - os=-elf + os= + obj=elf ;; tic54x-*) - os=-coff + os= + obj=coff ;; tic55x-*) - os=-coff + os= + obj=coff ;; tic6x-*) - os=-coff + os= + obj=coff ;; # This must come before the *-dec entry. pdp10-*) - os=-tops20 + os=tops20 ;; pdp11-*) - os=-none + os=none ;; *-dec | vax-*) - os=-ultrix4.2 + os=ultrix4.2 ;; m68*-apollo) - os=-domain + os=domain ;; i386-sun) - os=-sunos4.0.2 + os=sunos4.0.2 ;; m68000-sun) - os=-sunos3 + os=sunos3 ;; m68*-cisco) - os=-aout + os= + obj=aout ;; mep-*) - os=-elf + os= + obj=elf + ;; + # The -sgi and -siemens entries must be before the mips- entry + # or we get the wrong os. + *-sgi) + os=irix + ;; + *-siemens) + os=sysv4 ;; mips*-cisco) - os=-elf + os= + obj=elf ;; - mips*-*) - os=-elf + mips*-*|nanomips*-*) + os= + obj=elf ;; or32-*) - os=-coff + os= + obj=coff ;; - *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 + # This must be before the sparc-* entry or we get the wrong os. + *-tti) + os=sysv3 ;; sparc-* | *-sun) - os=-sunos4.1.1 + os=sunos4.1.1 ;; - *-be) - os=-beos + pru-*) + os= + obj=elf ;; - *-haiku) - os=-haiku + *-be) + os=beos ;; *-ibm) - os=-aix + os=aix ;; *-knuth) - os=-mmixware + os=mmixware ;; *-wec) - os=-proelf + os=proelf ;; *-winbond) - os=-proelf + os=proelf ;; *-oki) - os=-proelf + os=proelf ;; *-hp) - os=-hpux + os=hpux ;; *-hitachi) - os=-hiux + os=hiuxwe2 ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv + os=sysv ;; *-cbm) - os=-amigaos + os=amigaos ;; *-dg) - os=-dgux + os=dgux ;; *-dolphin) - os=-sysv3 + os=sysv3 ;; m68k-ccur) - os=-rtu + os=rtu ;; m88k-omron*) - os=-luna + os=luna ;; - *-next ) - os=-nextstep + *-next) + os=nextstep ;; *-sequent) - os=-ptx + os=ptx ;; *-crds) - os=-unos + os=unos ;; *-ns) - os=-genix + os=genix ;; i370-*) - os=-mvs - ;; - *-next) - os=-nextstep3 + os=mvs ;; *-gould) - os=-sysv + os=sysv ;; *-highlevel) - os=-bsd + os=bsd ;; *-encore) - os=-bsd - ;; - *-sgi) - os=-irix - ;; - *-siemens) - os=-sysv4 + os=bsd ;; *-masscomp) - os=-rtu + os=rtu ;; f30[01]-fujitsu | f700-fujitsu) - os=-uxpv + os=uxpv ;; *-rom68k) - os=-coff + os= + obj=coff ;; *-*bug) - os=-coff + os= + obj=coff ;; *-apple) - os=-macos + os=macos ;; *-atari*) - os=-mint + os=mint + ;; + *-wrs) + os=vxworks ;; *) - os=-none + os=none ;; esac + fi +# Now, validate our (potentially fixed-up) individual pieces (OS, OBJ). + +case $os in + # Sometimes we do "kernel-libc", so those need to count as OSes. + llvm* | musl* | newlib* | relibc* | uclibc*) + ;; + # Likewise for "kernel-abi" + eabi* | gnueabi*) + ;; + # VxWorks passes extra cpu info in the 4th filed. + simlinux | simwindows | spe) + ;; + # See `case $cpu-$os` validation below + ghcjs) + ;; + # Now accept the basic system types. + # Each alternative MUST end in a * to match a version number. + abug \ + | aix* \ + | amdhsa* \ + | amigados* \ + | amigaos* \ + | android* \ + | aof* \ + | aos* \ + | aros* \ + | atheos* \ + | auroraux* \ + | aux* \ + | beos* \ + | bitrig* \ + | bme* \ + | bosx* \ + | bsd* \ + | cegcc* \ + | chorusos* \ + | chorusrdb* \ + | clix* \ + | cloudabi* \ + | cnk* \ + | conix* \ + | cos* \ + | cxux* \ + | cygwin* \ + | darwin* \ + | dgux* \ + | dicos* \ + | dnix* \ + | domain* \ + | dragonfly* \ + | drops* \ + | ebmon* \ + | ecoff* \ + | ekkobsd* \ + | emscripten* \ + | emx* \ + | es* \ + | fiwix* \ + | freebsd* \ + | fuchsia* \ + | genix* \ + | genode* \ + | glidix* \ + | gnu* \ + | go32* \ + | haiku* \ + | hcos* \ + | hiux* \ + | hms* \ + | hpux* \ + | ieee* \ + | interix* \ + | ios* \ + | iris* \ + | irix* \ + | ironclad* \ + | isc* \ + | its* \ + | l4re* \ + | libertybsd* \ + | lites* \ + | lnews* \ + | luna* \ + | lynxos* \ + | mach* \ + | macos* \ + | magic* \ + | mbr* \ + | midipix* \ + | midnightbsd* \ + | mingw32* \ + | mingw64* \ + | minix* \ + | mint* \ + | mirbsd* \ + | mks* \ + | mlibc* \ + | mmixware* \ + | mon960* \ + | morphos* \ + | moss* \ + | moxiebox* \ + | mpeix* \ + | mpw* \ + | msdos* \ + | msys* \ + | mvs* \ + | nacl* \ + | netbsd* \ + | netware* \ + | newsos* \ + | nextstep* \ + | nindy* \ + | nonstopux* \ + | nova* \ + | nsk* \ + | nucleus* \ + | nx6 \ + | nx7 \ + | oabi* \ + | ohos* \ + | onefs* \ + | openbsd* \ + | openedition* \ + | openstep* \ + | os108* \ + | os2* \ + | os400* \ + | os68k* \ + | os9* \ + | ose* \ + | osf* \ + | oskit* \ + | osx* \ + | palmos* \ + | phoenix* \ + | plan9* \ + | powermax* \ + | powerunix* \ + | proelf* \ + | psos* \ + | psp* \ + | ptx* \ + | pw32* \ + | qnx* \ + | rdos* \ + | redox* \ + | rhapsody* \ + | riscix* \ + | riscos* \ + | rtems* \ + | rtmk* \ + | rtu* \ + | scout* \ + | secbsd* \ + | sei* \ + | serenity* \ + | sim* \ + | skyos* \ + | solaris* \ + | solidbsd* \ + | sortix* \ + | storm-chaos* \ + | sunos \ + | sunos[34]* \ + | superux* \ + | syllable* \ + | sym* \ + | sysv* \ + | tenex* \ + | tirtos* \ + | toppers* \ + | tops10* \ + | tops20* \ + | tpf* \ + | tvos* \ + | twizzler* \ + | uclinux* \ + | udi* \ + | udk* \ + | ultrix* \ + | unicos* \ + | uniplus* \ + | unleashed* \ + | unos* \ + | uwin* \ + | uxpv* \ + | v88r* \ + |*vms* \ + | vos* \ + | vsta* \ + | vxsim* \ + | vxworks* \ + | wasi* \ + | watchos* \ + | wince* \ + | windiss* \ + | windows* \ + | winnt* \ + | xenix* \ + | xray* \ + | zephyr* \ + | zvmoe* ) + ;; + # This one is extra strict with allowed versions + sco3.2v2 | sco3.2v[4-9]* | sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + ;; + # This refers to builds using the UEFI calling convention + # (which depends on the architecture) and PE file format. + # Note that this is both a different calling convention and + # different file format than that of GNU-EFI + # (x86_64-w64-mingw32). + uefi) + ;; + none) + ;; + kernel* | msvc* ) + # Restricted further below + ;; + '') + if test x"$obj" = x + then + echo "Invalid configuration '$1': Blank OS only allowed with explicit machine code file format" 1>&2 + fi + ;; + *) + echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2 + exit 1 + ;; +esac + +case $obj in + aout* | coff* | elf* | pe*) + ;; + '') + # empty is fine + ;; + *) + echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2 + exit 1 + ;; +esac + +# Here we handle the constraint that a (synthetic) cpu and os are +# valid only in combination with each other and nowhere else. +case $cpu-$os in + # The "javascript-unknown-ghcjs" triple is used by GHC; we + # accept it here in order to tolerate that, but reject any + # variations. + javascript-ghcjs) + ;; + javascript-* | *-ghcjs) + echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2 + exit 1 + ;; +esac + +# As a final step for OS-related things, validate the OS-kernel combination +# (given a valid OS), if there is a kernel. +case $kernel-$os-$obj in + linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \ + | linux-mlibc*- | linux-musl*- | linux-newlib*- \ + | linux-relibc*- | linux-uclibc*- | linux-ohos*- ) + ;; + uclinux-uclibc*- | uclinux-gnu*- ) + ;; + managarm-mlibc*- | managarm-kernel*- ) + ;; + windows*-msvc*-) + ;; + -dietlibc*- | -llvm*- | -mlibc*- | -musl*- | -newlib*- | -relibc*- \ + | -uclibc*- ) + # These are just libc implementations, not actual OSes, and thus + # require a kernel. + echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2 + exit 1 + ;; + -kernel*- ) + echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2 + exit 1 + ;; + *-kernel*- ) + echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2 + exit 1 + ;; + *-msvc*- ) + echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2 + exit 1 + ;; + kfreebsd*-gnu*- | knetbsd*-gnu*- | netbsd*-gnu*- | kopensolaris*-gnu*-) + ;; + vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-) + ;; + nto-qnx*-) + ;; + os2-emx-) + ;; + rtmk-nova-) + ;; + *-eabi*- | *-gnueabi*-) + ;; + none--*) + # None (no kernel, i.e. freestanding / bare metal), + # can be paired with an machine code file format + ;; + -*-) + # Blank kernel with real OS is always fine. + ;; + --*) + # Blank kernel and OS with real machine code file format is always fine. + ;; + *-*-*) + echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2 + exit 1 + ;; +esac + # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) - case $os in - -riscix*) +case $vendor in + unknown) + case $cpu-$os in + *-riscix*) vendor=acorn ;; - -sunos*) + *-sunos* | *-solaris*) vendor=sun ;; - -cnk*|-aix*) + *-cnk* | *-aix*) vendor=ibm ;; - -beos*) + *-beos*) vendor=be ;; - -hpux*) + *-hpux*) vendor=hp ;; - -mpeix*) + *-mpeix*) vendor=hp ;; - -hiux*) + *-hiux*) vendor=hitachi ;; - -unos*) + *-unos*) vendor=crds ;; - -dgux*) + *-dgux*) vendor=dg ;; - -luna*) + *-luna*) vendor=omron ;; - -genix*) + *-genix*) vendor=ns ;; - -mvs* | -opened*) + *-clix*) + vendor=intergraph + ;; + *-mvs* | *-opened*) + vendor=ibm + ;; + *-os400*) vendor=ibm ;; - -os400*) + s390-* | s390x-*) vendor=ibm ;; - -ptx*) + *-ptx*) vendor=sequent ;; - -tpf*) + *-tpf*) vendor=ibm ;; - -vxsim* | -vxworks* | -windiss*) + *-vxsim* | *-vxworks* | *-windiss*) vendor=wrs ;; - -aux*) + *-aux*) vendor=apple ;; - -hms*) + *-hms*) vendor=hitachi ;; - -mpw* | -macos*) + *-mpw* | *-macos*) vendor=apple ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) vendor=atari ;; - -vos*) + *-vos*) vendor=stratus ;; esac - basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac -echo $basic_machine$os +echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}" exit # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" diff --git a/third_party/webrtc/PJSIP_NOTES b/third_party/webrtc/PJSIP_NOTES index 31963d6f80..9dc335d124 100644 --- a/third_party/webrtc/PJSIP_NOTES +++ b/third_party/webrtc/PJSIP_NOTES @@ -53,4 +53,20 @@ index 9385befc96..308f5fe982 100644 +#endif #else #error Please add support for your architecture in typedefs.h - #endif \ No newline at end of file + #endif + +4. Add support for loongarch64 +diff --git a/third_party/webrtc/src/webrtc/typedefs.h b/third_party/webrtc/src/webrtc/typedefs.h +index 9a565df8b..8a5c830a1 100644 +--- a/third_party/webrtc/src/webrtc/typedefs.h ++++ b/third_party/webrtc/src/webrtc/typedefs.h +@@ -56,6 +56,9 @@ + #else + #define WEBRTC_ARCH_32_BITS + #endif ++#elif defined(__loongarch64) || defined(__loongarch64__) ++#define WEBRTC_ARCH_LITTLE_ENDIAN ++#define WEBRTC_ARCH_64_BITS + #else + #error Please add support for your architecture in typedefs.h + #endif diff --git a/third_party/webrtc/src/webrtc/typedefs.h b/third_party/webrtc/src/webrtc/typedefs.h index 9a565df8b7..8a5c830a1c 100644 --- a/third_party/webrtc/src/webrtc/typedefs.h +++ b/third_party/webrtc/src/webrtc/typedefs.h @@ -56,6 +56,9 @@ #else #define WEBRTC_ARCH_32_BITS #endif +#elif defined(__loongarch64) || defined(__loongarch64__) +#define WEBRTC_ARCH_LITTLE_ENDIAN +#define WEBRTC_ARCH_64_BITS #else #error Please add support for your architecture in typedefs.h #endif diff --git a/third_party/webrtc_aec3/PJSIP_NOTES b/third_party/webrtc_aec3/PJSIP_NOTES index 9adb739fb7..c3c9d7232e 100644 --- a/third_party/webrtc_aec3/PJSIP_NOTES +++ b/third_party/webrtc_aec3/PJSIP_NOTES @@ -78,3 +78,19 @@ index d76d35167..2d8b1037f 100644 # include typedef float32x4_t v4sf; # define SIMD_SZ 4 + +6. Add support for loongarch64 +diff --git a/third_party/webrtc_aec3/src/rtc_base/system/arch.h b/third_party/webrtc_aec3/src/rtc_base/system/arch.h +index be2367b85..62aaba88b 100644 +--- a/third_party/webrtc_aec3/src/rtc_base/system/arch.h ++++ b/third_party/webrtc_aec3/src/rtc_base/system/arch.h +@@ -73,6 +73,9 @@ + #elif defined(__riscv) && __riscv_xlen == 32 + #define WEBRTC_ARCH_32_BITS + #define WEBRTC_ARCH_LITTLE_ENDIAN ++#elif defined(__loongarch64) || defined(__loongarch64__) ++#define WEBRTC_ARCH_LITTLE_ENDIAN ++#define WEBRTC_ARCH_64_BITS + #elif defined(__pnacl__) + #define WEBRTC_ARCH_32_BITS + #define WEBRTC_ARCH_LITTLE_ENDIAN diff --git a/third_party/webrtc_aec3/src/rtc_base/system/arch.h b/third_party/webrtc_aec3/src/rtc_base/system/arch.h index be2367b85f..62aaba88bd 100644 --- a/third_party/webrtc_aec3/src/rtc_base/system/arch.h +++ b/third_party/webrtc_aec3/src/rtc_base/system/arch.h @@ -73,6 +73,9 @@ #elif defined(__riscv) && __riscv_xlen == 32 #define WEBRTC_ARCH_32_BITS #define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(__loongarch64) || defined(__loongarch64__) +#define WEBRTC_ARCH_LITTLE_ENDIAN +#define WEBRTC_ARCH_64_BITS #elif defined(__pnacl__) #define WEBRTC_ARCH_32_BITS #define WEBRTC_ARCH_LITTLE_ENDIAN From 214b6093c44dd8d9f07caa59701e92c3a78f9f6a Mon Sep 17 00:00:00 2001 From: Amilcar Ubiera Date: Mon, 14 Apr 2025 04:15:38 -0400 Subject: [PATCH 240/491] pool_i.h: Fix to use proper signed test against size. (#4391) --- pjlib/include/pj/pool_i.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pjlib/include/pj/pool_i.h b/pjlib/include/pj/pool_i.h index 6226a548d2..1e47257929 100644 --- a/pjlib/include/pj/pool_i.h +++ b/pjlib/include/pj/pool_i.h @@ -57,7 +57,7 @@ PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_block *block, pj_size_t alignme //} ptr = PJ_POOL_ALIGN_PTR(block->cur, alignment); if (block->cur <= ptr && /* check pointer overflow */ - (pj_size_t)(block->end - ptr) >= size) /* check available size */ + block->end - ptr >= (pj_ssize_t)size) /* check available size */ { block->cur = ptr + size; return ptr; From 65eb6df6d3e64d36c1241ecd4ac0ccbd909e76a3 Mon Sep 17 00:00:00 2001 From: Riza Sulistyo Date: Wed, 16 Apr 2025 16:38:00 +0700 Subject: [PATCH 241/491] Check return value when setting ALSA format (#4397) * Check return value when setting ALSA format * Add snd_pcm_close() * Add more checks * Let the sound device opened when snd_pcm_hw_params_set_buffer_size_near() return error * Modify logging info --- pjmedia/src/pjmedia-audiodev/alsa_dev.c | 180 +++++++++++++++++++----- 1 file changed, 145 insertions(+), 35 deletions(-) diff --git a/pjmedia/src/pjmedia-audiodev/alsa_dev.c b/pjmedia/src/pjmedia-audiodev/alsa_dev.c index f326c13f5e..4a7795d826 100755 --- a/pjmedia/src/pjmedia-audiodev/alsa_dev.c +++ b/pjmedia/src/pjmedia-audiodev/alsa_dev.c @@ -662,18 +662,36 @@ static pj_status_t open_playback (struct alsa_stream* stream, stream->af->devs[param->play_id].name, SND_PCM_STREAM_PLAYBACK, 0); - if (result < 0) + if (result < 0) { + PJ_LOG (3,(THIS_FILE, "Unable to open playback device '%s', err: %s", + stream->af->devs[param->play_id].name, snd_strerror(result))); + return PJMEDIA_EAUD_SYSERR; + } /* Allocate a hardware parameters object. */ snd_pcm_hw_params_alloca (¶ms); /* Fill it in with default values. */ - snd_pcm_hw_params_any (stream->pb_pcm, params); + result = snd_pcm_hw_params_any (stream->pb_pcm, params); + if (result < 0) { + PJ_LOG (3,(THIS_FILE, "Unable to get configuration for " + "playback device '%s', err: %s", + stream->af->devs[param->play_id].name, snd_strerror(result))); + + goto on_error; + } /* Set interleaved mode */ - snd_pcm_hw_params_set_access (stream->pb_pcm, params, - SND_PCM_ACCESS_RW_INTERLEAVED); + result = snd_pcm_hw_params_set_access (stream->pb_pcm, params, + SND_PCM_ACCESS_RW_INTERLEAVED); + if (result < 0) { + PJ_LOG (3,(THIS_FILE, "Unable to set interleaved mode for " + "playback device '%s', err: %s", + stream->af->devs[param->play_id].name, snd_strerror(result))); + + goto on_error; + } /* Set format */ switch (param->bits_per_sample) { @@ -698,7 +716,14 @@ static pj_status_t open_playback (struct alsa_stream* stream, format = SND_PCM_FORMAT_S16_LE; break; } - snd_pcm_hw_params_set_format (stream->pb_pcm, params, format); + result = snd_pcm_hw_params_set_format (stream->pb_pcm, params, format); + if (result < 0) { + PJ_LOG (3,(THIS_FILE, "Unable to set format %d for " + "playback device '%s', err: %s", format, + stream->af->devs[param->play_id].name, snd_strerror(result))); + + goto on_error; + } /* Set number of channels */ TRACE_((THIS_FILE, "open_playback: set channels: %d", @@ -707,16 +732,26 @@ static pj_status_t open_playback (struct alsa_stream* stream, param->channel_count); if (result < 0) { PJ_LOG (3,(THIS_FILE, "Unable to set a channel count of %d for " - "playback device '%s'", param->channel_count, - stream->af->devs[param->play_id].name)); - snd_pcm_close (stream->pb_pcm); - return PJMEDIA_EAUD_SYSERR; + "playback device '%s', err: %s", param->channel_count, + stream->af->devs[param->play_id].name,snd_strerror(result))); + + goto on_error; } /* Set clock rate */ rate = param->clock_rate; TRACE_((THIS_FILE, "open_playback: set clock rate: %d", rate)); - snd_pcm_hw_params_set_rate_near (stream->pb_pcm, params, &rate, NULL); + result = snd_pcm_hw_params_set_rate_near (stream->pb_pcm, params, + &rate, NULL); + if (result < 0) { + PJ_LOG (3,(THIS_FILE, "Unable to set clock rate: %d for " + "playback device '%s', err: %s", param->clock_rate, + stream->af->devs[param->play_id].name, + snd_strerror(result))); + + goto on_error; + } + TRACE_((THIS_FILE, "open_playback: clock rate set to: %d", rate)); /* Set period size to samples_per_frame frames. */ @@ -725,13 +760,21 @@ static pj_status_t open_playback (struct alsa_stream* stream, TRACE_((THIS_FILE, "open_playback: set period size: %d", stream->pb_frames)); tmp_period_size = stream->pb_frames; - snd_pcm_hw_params_set_period_size_near (stream->pb_pcm, params, - &tmp_period_size, NULL); + result = snd_pcm_hw_params_set_period_size_near (stream->pb_pcm, params, + &tmp_period_size, NULL); + if (result < 0) { + PJ_LOG (3,(THIS_FILE, "Unable to set period size: %d for " + "playback device '%s', err: %s", (int)stream->pb_frames, + stream->af->devs[param->play_id].name, snd_strerror(result))); + + goto on_error; + } + /* Commenting this as it may cause the number of samples per frame * to be incorrest. - */ + */ // stream->pb_frames = tmp_period_size > stream->pb_frames ? - // tmp_period_size : stream->pb_frames; + // tmp_period_size : stream->pb_frames; TRACE_((THIS_FILE, "open_playback: period size set to: %d", tmp_period_size)); @@ -744,6 +787,12 @@ static pj_status_t open_playback (struct alsa_stream* stream, tmp_buf_size = tmp_period_size * 2; snd_pcm_hw_params_set_buffer_size_near (stream->pb_pcm, params, &tmp_buf_size); + if (result < 0) { + PJ_LOG (3,(THIS_FILE, "Warning: unable to set period size: %d for " + "playback device '%s', err: %s", (int)tmp_buf_size, + stream->af->devs[param->play_id].name, snd_strerror(result))); + } + stream->param.output_latency_ms = tmp_buf_size / (rate / 1000); /* Set our buffer */ @@ -759,8 +808,11 @@ static pj_status_t open_playback (struct alsa_stream* stream, /* Activate the parameters */ result = snd_pcm_hw_params (stream->pb_pcm, params); if (result < 0) { - snd_pcm_close (stream->pb_pcm); - return PJMEDIA_EAUD_SYSERR; + PJ_LOG (3,(THIS_FILE, "Unable to activate the param for " + "playback device '%s', err: %s", + stream->af->devs[param->play_id].name, snd_strerror(result))); + + goto on_error; } if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) { @@ -777,6 +829,10 @@ static pj_status_t open_playback (struct alsa_stream* stream, (int)stream->param.output_latency_ms)); return PJ_SUCCESS; + +on_error: + snd_pcm_close (stream->pb_pcm); + return PJMEDIA_EAUD_SYSERR; } @@ -797,21 +853,39 @@ static pj_status_t open_capture (struct alsa_stream* stream, PJ_LOG (5,(THIS_FILE, "open_capture: Open capture device '%s'", stream->af->devs[param->rec_id].name)); result = snd_pcm_open (&stream->ca_pcm, - stream->af->devs[param->rec_id].name, + stream->af->devs[param->rec_id].name, SND_PCM_STREAM_CAPTURE, 0); - if (result < 0) + if (result < 0) { + PJ_LOG (3,(THIS_FILE, "Unable to open capture device '%s', err: %s", + stream->af->devs[param->rec_id].name, snd_strerror(result))); + return PJMEDIA_EAUD_SYSERR; + } /* Allocate a hardware parameters object. */ snd_pcm_hw_params_alloca (¶ms); /* Fill it in with default values. */ - snd_pcm_hw_params_any (stream->ca_pcm, params); + result = snd_pcm_hw_params_any (stream->ca_pcm, params); + if (result < 0) { + PJ_LOG (3,(THIS_FILE, "Unable to get configuration for " + "capture device '%s', err: %s", + stream->af->devs[param->rec_id].name, snd_strerror(result))); + + goto on_error; + } /* Set interleaved mode */ - snd_pcm_hw_params_set_access (stream->ca_pcm, params, - SND_PCM_ACCESS_RW_INTERLEAVED); + result = snd_pcm_hw_params_set_access (stream->ca_pcm, params, + SND_PCM_ACCESS_RW_INTERLEAVED); + if (result < 0) { + PJ_LOG (3,(THIS_FILE, "Unable to set interleaved mode for " + "capture device '%s', err: %s", + stream->af->devs[param->rec_id].name, snd_strerror(result))); + + goto on_error; + } /* Set format */ switch (param->bits_per_sample) { @@ -836,7 +910,14 @@ static pj_status_t open_capture (struct alsa_stream* stream, format = SND_PCM_FORMAT_S16_LE; break; } - snd_pcm_hw_params_set_format (stream->ca_pcm, params, format); + result = snd_pcm_hw_params_set_format (stream->ca_pcm, params, format); + if (result < 0) { + PJ_LOG (3,(THIS_FILE, "Unable to set format %d for " + "capture device '%s', err: %s", format, + stream->af->devs[param->rec_id].name, snd_strerror(result))); + + goto on_error; + } /* Set number of channels */ TRACE_((THIS_FILE, "open_capture: set channels: %d", @@ -845,16 +926,25 @@ static pj_status_t open_capture (struct alsa_stream* stream, param->channel_count); if (result < 0) { PJ_LOG (3,(THIS_FILE, "Unable to set a channel count of %d for " - "capture device '%s'", param->channel_count, - stream->af->devs[param->rec_id].name)); - snd_pcm_close (stream->ca_pcm); - return PJMEDIA_EAUD_SYSERR; - } + "capture device '%s', err: %s", param->channel_count, + stream->af->devs[param->rec_id].name,snd_strerror(result))); + goto on_error; + } /* Set clock rate */ rate = param->clock_rate; TRACE_((THIS_FILE, "open_capture: set clock rate: %d", rate)); - snd_pcm_hw_params_set_rate_near (stream->ca_pcm, params, &rate, NULL); + result = snd_pcm_hw_params_set_rate_near (stream->ca_pcm, params, &rate, + NULL); + if (result < 0) { + PJ_LOG (3,(THIS_FILE, "Unable to set clock rate: %d for " + "capture device '%s', err: %s", param->clock_rate, + stream->af->devs[param->rec_id].name, + snd_strerror(result))); + + goto on_error; + } + TRACE_((THIS_FILE, "open_capture: clock rate set to: %d", rate)); /* Set period size to samples_per_frame frames. */ @@ -863,8 +953,15 @@ static pj_status_t open_capture (struct alsa_stream* stream, TRACE_((THIS_FILE, "open_capture: set period size: %d", stream->ca_frames)); tmp_period_size = stream->ca_frames; - snd_pcm_hw_params_set_period_size_near (stream->ca_pcm, params, - &tmp_period_size, NULL); + result = snd_pcm_hw_params_set_period_size_near (stream->ca_pcm, params, + &tmp_period_size, NULL); + if (result < 0) { + PJ_LOG (3,(THIS_FILE, "Unable to set period size: %d for " + "capture device '%s', err: %s", (int)stream->ca_frames, + stream->af->devs[param->rec_id].name, snd_strerror(result))); + + goto on_error; + } /* Commenting this as it may cause the number of samples per frame * to be incorrest. */ @@ -880,8 +977,14 @@ static pj_status_t open_capture (struct alsa_stream* stream, tmp_buf_size = (rate / 1000) * PJMEDIA_SND_DEFAULT_REC_LATENCY; if (tmp_buf_size < tmp_period_size * 2) tmp_buf_size = tmp_period_size * 2; - snd_pcm_hw_params_set_buffer_size_near (stream->ca_pcm, params, - &tmp_buf_size); + result = snd_pcm_hw_params_set_buffer_size_near (stream->ca_pcm, params, + &tmp_buf_size); + if (result < 0) { + PJ_LOG (3,(THIS_FILE, "Warning: unable to set period size: %d for " + "capture device '%s', err: %s", (int)tmp_buf_size, + stream->af->devs[param->rec_id].name, snd_strerror(result))); + } + stream->param.input_latency_ms = tmp_buf_size / (rate / 1000); /* Set our buffer */ @@ -897,8 +1000,11 @@ static pj_status_t open_capture (struct alsa_stream* stream, /* Activate the parameters */ result = snd_pcm_hw_params (stream->ca_pcm, params); if (result < 0) { - snd_pcm_close (stream->ca_pcm); - return PJMEDIA_EAUD_SYSERR; + PJ_LOG (3,(THIS_FILE, "Unable to activate the param for " + "capture device '%s', err: %s", + stream->af->devs[param->rec_id].name, snd_strerror(result))); + + goto on_error; } if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) { @@ -915,6 +1021,10 @@ static pj_status_t open_capture (struct alsa_stream* stream, (int)stream->param.input_latency_ms)); return PJ_SUCCESS; + +on_error: + snd_pcm_close (stream->ca_pcm); + return PJMEDIA_EAUD_SYSERR; } @@ -1098,7 +1208,7 @@ static pj_status_t alsa_stream_start (pjmedia_aud_stream *s) if (stream->param.dir & PJMEDIA_DIR_CAPTURE) { status = pj_thread_create (stream->pool, - "alsasound_playback", + "alsasound_capture", ca_thread_func, stream, 0, //ZERO, From 1fad08d37c5b22963ffc3d72429adec541578c1e Mon Sep 17 00:00:00 2001 From: ablangy <32962735+ablangy@users.noreply.github.com> Date: Thu, 17 Apr 2025 12:07:33 +0200 Subject: [PATCH 242/491] Issue #4400: pjsip_dialog not set in pjsip_tx_data clone (#4401) A pjsip_tx_data cloned by pjsip_inv_answer() does not have a reference to the pjsip_dialog of the source pjsip_tx_data. --- pjsip/src/pjsip-ua/sip_inv.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c index 0ed94be75c..348b73349d 100644 --- a/pjsip/src/pjsip-ua/sip_inv.c +++ b/pjsip/src/pjsip-ua/sip_inv.c @@ -2772,6 +2772,9 @@ PJ_DEF(pj_status_t) pjsip_inv_answer( pjsip_inv_session *inv, if (status != PJ_SUCCESS) goto on_return; + /* Put the dialog of the session in tdata's mod_data */ + last_res->mod_data[inv->dlg->ua->id] = inv->dlg; + /* Modify last response. */ status = pjsip_dlg_modify_response(inv->dlg, last_res, st_code, st_text); if (status != PJ_SUCCESS) { From 4b9d5336d2c98ca8ecd86ed17b3488aba6b03633 Mon Sep 17 00:00:00 2001 From: jimying Date: Thu, 17 Apr 2025 18:07:55 +0800 Subject: [PATCH 243/491] remove dead code (#4392) There are some functions declared but not implemented: pjsip_endpt_find_tsx() pjsip_endpt_register_tsx() pjsip_endpt_destroy_tsx() pjsip_endpt_send_tsx_event() --- pjsip/include/pjsip/sip_endpoint.h | 42 ------------------------------ 1 file changed, 42 deletions(-) diff --git a/pjsip/include/pjsip/sip_endpoint.h b/pjsip/include/pjsip/sip_endpoint.h index 4a62282334..5a884895e1 100644 --- a/pjsip/include/pjsip/sip_endpoint.h +++ b/pjsip/include/pjsip/sip_endpoint.h @@ -352,39 +352,6 @@ PJ_DECL(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt, PJ_DECL(void) pjsip_endpt_release_pool( pjsip_endpoint *endpt, pj_pool_t *pool ); -/** - * Find transaction in endpoint's transaction table by the transaction's key. - * This function normally is only used by modules. The key for a transaction - * can be created by calling #pjsip_tsx_create_key. - * - * @param endpt The endpoint instance. - * @param key Transaction key, as created with #pjsip_tsx_create_key. - * - * @return The transaction, or NULL if it's not found. - */ -PJ_DECL(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt, - const pj_str_t *key ); - -/** - * Register the transaction to the endpoint's transaction table. - * This function should only be used internally by the stack. - * - * @param endpt The SIP endpoint. - * @param tsx The transaction. - */ -PJ_DECL(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt, - pjsip_transaction *tsx); - -/** - * Forcefull destroy the transaction. This function should only be used - * internally by the stack. - * - * @param endpt The endpoint. - * @param tsx The transaction to destroy. - */ -PJ_DECL(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt, - pjsip_transaction *tsx); - /** * Create a new transmit data buffer. * This function, like all other endpoint functions, is thread safe. @@ -700,15 +667,6 @@ PJ_DECL(void) pjsip_endpt_log_error( pjsip_endpoint *endpt, PJ_LOG(4,expr); \ } while (0) -/* - * Internal functions. - */ -/** - * Internal: receive transaction events from transactions and put in the - * event queue to be processed later. - */ -void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt ); - PJ_END_DECL #endif /* __PJSIP_SIP_ENDPOINT_H__ */ From 6dd67c37d3358d15bccce4534e8c0bac533dab33 Mon Sep 17 00:00:00 2001 From: jimying Date: Wed, 23 Apr 2025 19:24:37 +0800 Subject: [PATCH 244/491] remove dead code (#4406) --- pjsip-apps/src/samples/footprint.c | 1 - pjsip/include/pjsip-ua/sip_regc.h | 9 --------- 2 files changed, 10 deletions(-) diff --git a/pjsip-apps/src/samples/footprint.c b/pjsip-apps/src/samples/footprint.c index 7d889aa98a..8f4678fdee 100644 --- a/pjsip-apps/src/samples/footprint.c +++ b/pjsip-apps/src/samples/footprint.c @@ -378,7 +378,6 @@ int dummy_function() #endif #ifdef HAS_PJSIP_REGC - //pjsip_regc_get_module(); pjsip_regc_create(NULL, NULL, NULL, NULL); pjsip_regc_destroy(NULL); pjsip_regc_get_info(NULL, NULL); diff --git a/pjsip/include/pjsip-ua/sip_regc.h b/pjsip/include/pjsip-ua/sip_regc.h index ee001306e6..fc8afbe8cb 100644 --- a/pjsip/include/pjsip-ua/sip_regc.h +++ b/pjsip/include/pjsip-ua/sip_regc.h @@ -127,15 +127,6 @@ struct pjsip_regc_info */ typedef struct pjsip_regc_info pjsip_regc_info; - -/** - * Get the module instance for client registration module. - * - * @return client registration module. - */ -PJ_DECL(pjsip_module*) pjsip_regc_get_module(void); - - /** * Create client registration structure. * From 5b40b8722245ef0b8e9ac756bd4ec182d840c6a8 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Wed, 23 Apr 2025 18:25:04 +0700 Subject: [PATCH 245/491] Update WebRTC docs (#4405) --- pjmedia/include/pjmedia/config.h | 15 ++++++++++++++- pjmedia/include/pjmedia/echo.h | 10 ++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h index a13204aa2a..5730c0786d 100644 --- a/pjmedia/include/pjmedia/config.h +++ b/pjmedia/include/pjmedia/config.h @@ -735,13 +735,26 @@ /** - * WebRtc Acoustic Echo Cancellation (AEC). + * WebRTC Acoustic Echo Cancellation (AEC). + * Please check https://github.com/pjsip/pjproject/issues/1888 for more info. + * * By default is disabled. */ #ifndef PJMEDIA_HAS_WEBRTC_AEC # define PJMEDIA_HAS_WEBRTC_AEC 0 #endif +/** + * WebRTC Acoustic Echo Cancellation 3 (WebRTC AEC3). + * Please check https://github.com/pjsip/pjproject/pull/2722 and + * https://github.com/pjsip/pjproject/pull/2775 for more info. + * + * By default is disabled. + */ +#ifndef PJMEDIA_HAS_WEBRTC_AEC3 +# define PJMEDIA_HAS_WEBRTC_AEC3 0 +#endif + /** * Specify whether WebRtc EC should use its mobile version AEC. * diff --git a/pjmedia/include/pjmedia/echo.h b/pjmedia/include/pjmedia/echo.h index 9045918217..1c92e03142 100644 --- a/pjmedia/include/pjmedia/echo.h +++ b/pjmedia/include/pjmedia/echo.h @@ -115,12 +115,16 @@ typedef enum pjmedia_echo_flag /** * If PJMEDIA_ECHO_USE_NOISE_SUPPRESSOR flag is specified, the echo * canceller will also apply noise suppressor method to reduce noise. + * + * Currently this is only effective on WebRTC AEC & WebRTC AEC3 backends. */ PJMEDIA_ECHO_USE_NOISE_SUPPRESSOR = 128, /** * If PJMEDIA_ECHO_USE_GAIN_CONTROLLER flag is specified, the echo * canceller will also apply automatic gain control. + * + * Currently this is only effective on WebRTC AEC3 backend. */ PJMEDIA_ECHO_USE_GAIN_CONTROLLER = 256, @@ -135,6 +139,8 @@ typedef enum pjmedia_echo_flag * Use conservative aggressiveness setting for the echo canceller * algorithm. This setting is mutually exclusive with the other * aggressiveness settings. + * + * Currently this is only effective on WebRTC AEC backend. */ PJMEDIA_ECHO_AGGRESSIVENESS_CONSERVATIVE = 0x1000, @@ -142,6 +148,8 @@ typedef enum pjmedia_echo_flag * Use moderate aggressiveness setting for the echo canceller algorithm. * This setting is mutually exclusive with the other aggressiveness * settings. + * + * Currently this is only effective on WebRTC AEC backend. */ PJMEDIA_ECHO_AGGRESSIVENESS_MODERATE = 0x2000, @@ -149,6 +157,8 @@ typedef enum pjmedia_echo_flag * Use aggressive aggressiveness setting for the echo canceller * algorithm. This setting is mutually exclusive with the other * aggressiveness settings. + * + * Currently this is only effective on WebRTC AEC backend. */ PJMEDIA_ECHO_AGGRESSIVENESS_AGGRESSIVE = 0x3000, From 82d63231973134009b86181d8b11f4fc63bacc5a Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Fri, 25 Apr 2025 08:54:21 +0700 Subject: [PATCH 246/491] More logging for WebRTC-AEC3 (#4407) --- pjmedia/src/pjmedia/echo_webrtc_aec3.cpp | 57 +++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/pjmedia/src/pjmedia/echo_webrtc_aec3.cpp b/pjmedia/src/pjmedia/echo_webrtc_aec3.cpp index 0bef72d54f..7194d50815 100644 --- a/pjmedia/src/pjmedia/echo_webrtc_aec3.cpp +++ b/pjmedia/src/pjmedia/echo_webrtc_aec3.cpp @@ -34,6 +34,7 @@ #include "modules/audio_processing/ns/noise_suppressor.h" #include "modules/audio_processing/gain_controller2.h" #include "modules/audio_processing/audio_buffer.h" +#include "rtc_base/logging.h" using namespace webrtc; @@ -41,6 +42,10 @@ using namespace webrtc; #define THIS_FILE "echo_webrtc_aec3.cpp" +#define TRACE_WEBRTC 0 + +class PjLogSink; + typedef struct webrtc_ec { unsigned options; @@ -58,9 +63,44 @@ typedef struct webrtc_ec GainController2 *agc; AudioBuffer *cap_buf; AudioBuffer *rend_buf; + + PjLogSink *log_sink; } webrtc_ec; +class PjLogSink : public rtc::LogSink +{ +public: + void OnLogMessage(const std::string& message, + rtc::LoggingSeverity severity) override + { + /* Trim new line */ + std::string s = message; + s.erase(s.find_last_not_of("\n\r")+1); + + switch (severity) { + case rtc::LoggingSeverity::LS_ERROR: + PJ_LOG(3, ("webrtc-aec3", "%s", s.c_str())); + break; + case rtc::LoggingSeverity::LS_WARNING: + PJ_LOG(4,("webrtc-aec3", "%s", s.c_str())); + break; + default: + PJ_LOG(5,("webrtc-aec3", "%s", s.c_str())); + break; + } + } + + void OnLogMessage(const std::string& message) override + { + /* Trim new line */ + std::string s = message; + s.erase(s.find_last_not_of("\n\r")+1); + + PJ_LOG(5,("webrtc-aec3", "%s", s.c_str())); + } +}; + /* * Create the AEC. */ @@ -95,6 +135,12 @@ PJ_DEF(pj_status_t) webrtc_aec3_create(pj_pool_t *pool, echo->clock_rate = clock_rate; echo->frame_length = clock_rate/100; echo->num_bands = clock_rate/16000; + +#if TRACE_WEBRTC + echo->log_sink = new PjLogSink(); + rtc::LogMessage::AddLogToStream(echo->log_sink, + rtc::LoggingSeverity::LS_INFO); +#endif echo->aec = new EchoCanceller3(EchoCanceller3Config(), clock_rate, channel_count, channel_count); @@ -109,6 +155,7 @@ PJ_DEF(pj_status_t) webrtc_aec3_create(pj_pool_t *pool, /* Valid values are 6, 12, 18, 21 dB */ cfg.target_level = NsConfig::SuppressionLevel::k12dB; echo->ns = new NoiseSuppressor(cfg, clock_rate, channel_count); + PJ_LOG(5, (THIS_FILE, "WebRTC AEC3 has noise suppressor enabled")); } if (options & PJMEDIA_ECHO_USE_GAIN_CONTROLLER) { @@ -117,8 +164,10 @@ PJ_DEF(pj_status_t) webrtc_aec3_create(pj_pool_t *pool, AudioProcessing::Config::GainController2 cfg; cfg.adaptive_digital.enabled = true; - if (GainController2::Validate(cfg)) + if (GainController2::Validate(cfg)) { echo->agc->ApplyConfig(cfg); + PJ_LOG(5, (THIS_FILE, "WebRTC AEC3 has AGC enabled")); + } } /* Done */ @@ -157,6 +206,12 @@ PJ_DEF(pj_status_t) webrtc_aec3_destroy(void *state ) echo->rend_buf = NULL; } + if (echo->log_sink) { + rtc::LogMessage::RemoveLogToStream(echo->log_sink); + delete echo->log_sink; + echo->log_sink = NULL; + } + #if WEBRTC_LINUX == 1 && defined(WEBRTC_ARCH_ARM_V7) delete echo; #endif From d62dd9d5af76ea49763bd639d8951a1d6a4cb01e Mon Sep 17 00:00:00 2001 From: atsushi-ageet Date: Mon, 28 Apr 2025 12:06:02 +0900 Subject: [PATCH 247/491] File make with j option problem (#4408) --- build/rules.mak | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build/rules.mak b/build/rules.mak index a7735d08e9..ae139fba99 100644 --- a/build/rules.mak +++ b/build/rules.mak @@ -154,32 +154,32 @@ $(OBJDIR)/$(app).ko: $(OBJDIR)/$(app).o | $(OBJDIRS) ../lib/$(app).ko: $(LIB) $(OBJDIR)/$(app).ko cp $(OBJDIR)/$(app).ko ../lib -$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.m | $(OBJDIRS) +$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.m | $(@D) $(CC) $($(APP)_CFLAGS) \ $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ $(subst /,$(HOST_PSEP),$<) -$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.c | $(OBJDIRS) +$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.c | $(@D) $(CC) $($(APP)_CFLAGS) \ $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ $(subst /,$(HOST_PSEP),$<) -$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.S | $(OBJDIRS) +$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.S | $(@D) $(CC) $($(APP)_CFLAGS) \ $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ $(subst /,$(HOST_PSEP),$<) -$(OBJDIR)/dshowclasses.o: $(SRCDIR)/dshowclasses.cpp | $(OBJDIRS) +$(OBJDIR)/dshowclasses.o: $(SRCDIR)/dshowclasses.cpp | $(@D) $(CXX) $($(APP)_CXXFLAGS) -I$(SRCDIR)/../../../third_party/BaseClasses -fpermissive \ $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ $(subst /,$(HOST_PSEP),$<) -$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cpp | $(OBJDIRS) +$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cpp | $(@D) $(CXX) $($(APP)_CXXFLAGS) \ $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ $(subst /,$(HOST_PSEP),$<) -$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cc | $(OBJDIRS) +$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cc | $(@D) $(CXX) $($(APP)_CXXFLAGS) \ $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ $(subst /,$(HOST_PSEP),$<) From e9dcd8e75c08f4b856b39f87c3bab9221e0502c0 Mon Sep 17 00:00:00 2001 From: sauwming Date: Mon, 28 Apr 2025 16:06:39 +0800 Subject: [PATCH 248/491] Fixed buffer overflow in Android video codec (#4409) --- .../src/pjmedia-codec/and_vid_mediacodec.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/pjmedia/src/pjmedia-codec/and_vid_mediacodec.cpp b/pjmedia/src/pjmedia-codec/and_vid_mediacodec.cpp index e5c0e7501a..c5009ae19d 100644 --- a/pjmedia/src/pjmedia-codec/and_vid_mediacodec.cpp +++ b/pjmedia/src/pjmedia-codec/and_vid_mediacodec.cpp @@ -1386,7 +1386,7 @@ static pj_status_t and_media_decode(pjmedia_vid_codec *codec, { pj_status_t status = PJ_SUCCESS; pj_size_t output_size; - int len; + int len = 0; media_status_t am_status; and_med_buf_info buf_info; pj_uint8_t *output_buf; @@ -1490,12 +1490,16 @@ static pj_status_t and_media_decode(pjmedia_vid_codec *codec, PJ_LOG(4,(THIS_FILE, "Decoder getOutputBuffer failed")); return status; } - len = write_yuv((pj_uint8_t *)output->buf, - output->size, - output_buf, - and_media_data->dec_stride_len, - and_media_data->prm->dec_fmt.det.vid.size.w, - and_media_data->prm->dec_fmt.det.vid.size.h); + if (output->size >= and_media_data->prm->dec_fmt.det.vid.size.w * + and_media_data->prm->dec_fmt.det.vid.size.h * 3 / 2) + { + len = write_yuv((pj_uint8_t *)output->buf, + output->size, + output_buf, + and_media_data->dec_stride_len, + and_media_data->prm->dec_fmt.det.vid.size.w, + and_media_data->prm->dec_fmt.det.vid.size.h); + } am_status = AMediaCodec_releaseOutputBuffer(and_media_data->dec, buf_info.index, 0); From b9619cde081d034559d1d7ae5cd8305a872e439a Mon Sep 17 00:00:00 2001 From: sauwming Date: Tue, 29 Apr 2025 07:28:08 +0800 Subject: [PATCH 249/491] Fixed incorrect call count upon message sending failure when making call (#4410) --- pjsip/src/pjsua-lib/pjsua_call.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 35ffefc350..ab00b14039 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -573,9 +573,14 @@ on_make_call_med_tp_complete(pjsua_call_id call_id, if (status != PJ_SUCCESS) { cb_called = PJ_TRUE; - /* Upon failure to send first request, the invite - * session would have been cleared. + /* If call inv hasn't been cleared from on_call_state(DISCONNECTED), + * we clear it here. */ + if (call->inv) { + pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE); + --pjsua_var.call_cnt; + } + call->inv = inv = NULL; goto on_error; } From cfb5d257b6f5736cfd254be8f75adc8d91f06e4f Mon Sep 17 00:00:00 2001 From: sauwming Date: Tue, 29 Apr 2025 14:30:38 +0800 Subject: [PATCH 250/491] Use configured public address for SIP transport TCP and TLS (#4411) --- pjsip/include/pjsip/sip_transport.h | 2 ++ pjsip/src/pjsip/sip_transport_tcp.c | 19 ++++++++++++++++--- pjsip/src/pjsip/sip_transport_tls.c | 17 ++++++++++++++--- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h index 8c183e26e5..9ad84bc36b 100644 --- a/pjsip/include/pjsip/sip_transport.h +++ b/pjsip/include/pjsip/sip_transport.h @@ -856,6 +856,7 @@ struct pjsip_transport int addr_len; /**< Length of addresses. */ pj_sockaddr local_addr; /**< Bound address. */ + pj_bool_t has_addr_name; /**< Has config published name? */ pjsip_host_port local_name; /**< Published name (eg. STUN). */ pjsip_host_port remote_name; /**< Remote address name. */ pjsip_transport_dir dir; /**< Connection direction. */ @@ -1082,6 +1083,7 @@ struct pjsip_tpfactory char *info; /**< Transport info/description.*/ pj_sockaddr local_addr; /**< Bound address. */ + pj_bool_t has_addr_name; /**< Has published name? */ pjsip_host_port addr_name; /**< Published name. */ /** diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c index 51e847562a..db258d6488 100644 --- a/pjsip/src/pjsip/sip_transport_tcp.c +++ b/pjsip/src/pjsip/sip_transport_tcp.c @@ -290,6 +290,7 @@ static pj_status_t update_factory_addr(struct tcp_listener *listener, } /* Copy the address */ + listener->factory.has_addr_name = PJ_TRUE; listener->factory.addr_name = *addr_name; pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, &addr_name->host); @@ -674,7 +675,11 @@ static pj_status_t tcp_create( struct tcp_listener *listener, tcp->base.addr_len = pj_sockaddr_get_len(remote); pj_sockaddr_cp(&tcp->base.local_addr, local); - sockaddr_to_host_port(pool, &tcp->base.local_name, local); + + /* Use listener's published address, if any. */ + tcp->base.has_addr_name = listener->factory.has_addr_name; + tcp->base.local_name = listener->factory.addr_name; + sockaddr_to_host_port(pool, &tcp->base.remote_name, remote); tcp->base.dir = is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING; @@ -1078,8 +1083,16 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, pj_sockaddr_get_port(&local_addr) != 0) { pj_sockaddr_cp(tp_addr, &local_addr); - sockaddr_to_host_port(tcp->base.pool, &tcp->base.local_name, - &local_addr); + /* Only update published address if not specified, otherwise + * only update the port. + */ + if (!tcp->base.has_addr_name) { + sockaddr_to_host_port(tcp->base.pool, &tcp->base.local_name, + &local_addr); + } else { + tcp->base.local_name.port = + pj_sockaddr_get_port(&local_addr); + } } } diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c index 18f92eaca9..f74b22cb6d 100644 --- a/pjsip/src/pjsip/sip_transport_tls.c +++ b/pjsip/src/pjsip/sip_transport_tls.c @@ -402,6 +402,7 @@ static pj_status_t update_factory_addr(struct tls_listener *listener, } /* Copy the address */ + listener->factory.has_addr_name = PJ_TRUE; listener->factory.addr_name = *addr_name; pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, &addr_name->host); @@ -922,7 +923,10 @@ static pj_status_t tls_create( struct tls_listener *listener, pj_sockaddr_cp(&tls->base.local_addr, local); } - sockaddr_to_host_port(pool, &tls->base.local_name, &tls->base.local_addr); + /* Use listener's published address, if any. */ + tls->base.has_addr_name = listener->factory.has_addr_name; + tls->base.local_name = listener->factory.addr_name; + if (tls->remote_name.slen) { tls->base.remote_name.host = tls->remote_name; tls->base.remote_name.port = pj_sockaddr_get_port(remote); @@ -1366,8 +1370,15 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, new_port); } - sockaddr_to_host_port(tls->base.pool, &tls->base.local_name, - &tls->base.local_addr); + /* Only update published address if not specified, otherwise + * only update the port. + */ + if (!tls->base.has_addr_name) { + sockaddr_to_host_port(tls->base.pool, &tls->base.local_name, + &tls->base.local_addr); + } else { + tls->base.local_name.port = new_port; + } } PJ_LOG(4,(tls->base.obj_name, From 50889324b32411b29595595998a47b64b56848e0 Mon Sep 17 00:00:00 2001 From: sauwming Date: Mon, 5 May 2025 10:30:57 +0800 Subject: [PATCH 251/491] Prevent call hangup retry if call already terminated (#4417) --- pjsip/src/pjsua-lib/pjsua_call.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index ab00b14039..5389f08c09 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -3074,7 +3074,9 @@ static pj_status_t call_inv_end_session(pjsua_call *call, * pjsua_call_on_state_changed() to be called and call to be reset, * so we need to check for call->inv as well. */ - if (status != PJ_SUCCESS && call->inv) { + if (status != PJ_SUCCESS && status != PJSIP_ESESSIONTERMINATED && + call->inv) + { pj_time_val delay; /* Schedule a retry */ From caf29e7b9d3d889e183b74517c4042d46df63016 Mon Sep 17 00:00:00 2001 From: Florian Xaver <29056908+wosrediinanatour@users.noreply.github.com> Date: Wed, 7 May 2025 04:25:17 +0200 Subject: [PATCH 252/491] Support latest 3GPP TS 33102 (#4414) --- pjsip/include/pjsip/sip_auth_aka.h | 2 +- pjsip/src/pjsua-lib/pjsua_acc.c | 117 +++++++++++++++++++++++------ 2 files changed, 93 insertions(+), 26 deletions(-) diff --git a/pjsip/include/pjsip/sip_auth_aka.h b/pjsip/include/pjsip/sip_auth_aka.h index ba3f9ca4ef..7f0ca8b6fd 100644 --- a/pjsip/include/pjsip/sip_auth_aka.h +++ b/pjsip/include/pjsip/sip_auth_aka.h @@ -129,7 +129,7 @@ PJ_BEGIN_DECL /** * Length of permanent/subscriber Key (K) in bytes. */ -#define PJSIP_AKA_KLEN 16 +#define PJSIP_AKA_KLEN 32 /** * Length of AKA authentication code in bytes. diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index e7fac2e786..0bd90c1ab4 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -2728,10 +2728,7 @@ static pj_status_t pjsua_regc_init(int acc_id) pjsua_perror(THIS_FILE, "Unable to generate suitable Contact header" " for registration", status); - destroy_regc(acc, PJ_TRUE); - pj_pool_release(pool); - acc->regc = NULL; - return status; + goto on_return; } pj_strdup_with_null(acc->pool, &acc->contact, &tmp_contact); @@ -2748,37 +2745,72 @@ static pj_status_t pjsua_regc_init(int acc_id) pjsua_perror(THIS_FILE, "Client registration initialization error", status); - destroy_regc(acc, PJ_TRUE); - pj_pool_release(pool); - - return status; + goto on_return; } - pjsip_regc_set_reg_tsx_cb(acc->regc, regc_tsx_cb); + status = pjsip_regc_set_reg_tsx_cb(acc->regc, regc_tsx_cb); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, + "Failed setting registration callback", + status); + goto on_return; + } /* Set client registration's transport based on acc's config. */ pjsua_init_tpselector(acc_id, &tp_sel); - pjsip_regc_set_transport(acc->regc, &tp_sel); + status = pjsip_regc_set_transport(acc->regc, &tp_sel); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, + "Failed setting registration transport", + status); + goto on_return; + } if (acc->cfg.use_shared_auth) { - pjsip_regc_set_auth_sess(acc->regc, &acc->shared_auth_sess); + status = pjsip_regc_set_auth_sess(acc->regc, &acc->shared_auth_sess); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, + "Failed setting registration shared auth session", + status); + goto on_return; + } } - /* Set credentials - */ + /* Set credentials */ if (acc->cred_cnt) { - pjsip_regc_set_credentials( acc->regc, acc->cred_cnt, acc->cred); + status = pjsip_regc_set_credentials(acc->regc, acc->cred_cnt, + acc->cred); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, + "Failed setting credentials for registration", + status); + goto on_return; + } } /* Set delay before registration refresh */ - pjsip_regc_set_delay_before_refresh(acc->regc, + status = pjsip_regc_set_delay_before_refresh( + acc->regc, acc->cfg.reg_delay_before_refresh); + if (status != PJ_SUCCESS) { + /* Maybe too big, it will fallback to the default setting, + * just print warning. + */ + pjsua_perror(THIS_FILE, + "Warning: failed setting registration refresh delay", + status); + } /* Set authentication preference */ - pjsip_regc_set_prefs(acc->regc, &acc->cfg.auth_pref); + status = pjsip_regc_set_prefs(acc->regc, &acc->cfg.auth_pref); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, + "Failed setting registration auth preference", + status); + goto on_return; + } - /* Set route-set - */ + /* Set route-set */ if (acc->cfg.reg_use_proxy) { pjsip_route_hdr route_set; const pjsip_route_hdr *r; @@ -2807,12 +2839,25 @@ static pj_status_t pjsua_regc_init(int acc_id) } } - if (!pj_list_empty(&route_set)) - pjsip_regc_set_route_set( acc->regc, &route_set ); + if (!pj_list_empty(&route_set)) { + status = pjsip_regc_set_route_set( acc->regc, &route_set ); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, + "Failed setting registration route set", + status); + goto on_return; + } + } } /* Add custom request headers specified in the account config */ - pjsip_regc_add_headers(acc->regc, &acc->cfg.reg_hdr_list); + status = pjsip_regc_add_headers(acc->regc, &acc->cfg.reg_hdr_list); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, + "Failed setting registration custom headers", + status); + goto on_return; + } /* Add other request headers. */ if (pjsua_var.ua_cfg.user_agent.slen) { @@ -2826,7 +2871,15 @@ static pj_status_t pjsua_regc_init(int acc_id) &pjsua_var.ua_cfg.user_agent); pj_list_push_back(&hdr_list, (pjsip_hdr*)h); - pjsip_regc_add_headers(acc->regc, &hdr_list); + status = pjsip_regc_add_headers(acc->regc, &hdr_list); + if (status != PJ_SUCCESS) { + /* Informational header, just print warning */ + pjsua_perror(THIS_FILE, + "Warning: failed setting registration " + "user-agent header", + status); + status = PJ_SUCCESS; + } } /* If SIP outbound is used, add "Supported: outbound, path header" */ @@ -2844,12 +2897,26 @@ static pj_status_t pjsua_regc_init(int acc_id) hsup->values[0] = pj_str("outbound"); hsup->values[1] = pj_str("path"); - pjsip_regc_add_headers(acc->regc, &hdr_list); + status = pjsip_regc_add_headers(acc->regc, &hdr_list); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, + "Failed setting registration outbound support " + "indication", + status); + goto on_return; + } } - pj_pool_release(pool); +on_return: + if (status != PJ_SUCCESS) { + if (acc->regc) + destroy_regc(acc, PJ_TRUE); - return PJ_SUCCESS; + pjsua_perror(THIS_FILE, "Error initializing client registration", + status); + } + pj_pool_release(pool); + return status; } pj_bool_t pjsua_sip_acc_is_using_ipv6(pjsua_acc_id acc_id) From e131cae292081e3cc69449a89b89bbc47dfbff87 Mon Sep 17 00:00:00 2001 From: sauwming Date: Wed, 7 May 2025 12:21:40 +0800 Subject: [PATCH 253/491] Prevent data race between SSL handshake and data read (#4418) --- pjlib/src/pj/ssl_sock_imp_common.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pjlib/src/pj/ssl_sock_imp_common.c b/pjlib/src/pj/ssl_sock_imp_common.c index 811c960fd1..702ec0d72c 100644 --- a/pjlib/src/pj/ssl_sock_imp_common.c +++ b/pjlib/src/pj/ssl_sock_imp_common.c @@ -1174,9 +1174,16 @@ static pj_bool_t ssock_on_accept_complete (pj_ssl_sock_t *ssock_parent, } /* Start SSL handshake */ + /* Prevent data race with on_data_read() until ssl_do_handshake() + * completes. + */ + if (ssock->circ_buf_input_mutex) + pj_lock_acquire(ssock->circ_buf_input_mutex); ssock->ssl_state = SSL_STATE_HANDSHAKING; ssl_set_state(ssock, PJ_TRUE); status = ssl_do_handshake(ssock); + if (ssock->circ_buf_input_mutex) + pj_lock_release(ssock->circ_buf_input_mutex); on_return: if (ssock && status != PJ_EPENDING) { @@ -1501,8 +1508,8 @@ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool, return status; /* Create input circular buffer mutex */ - status = pj_lock_create_simple_mutex(pool, pool->obj_name, - &ssock->circ_buf_input_mutex); + status = pj_lock_create_recursive_mutex(pool, pool->obj_name, + &ssock->circ_buf_input_mutex); if (status != PJ_SUCCESS) return status; From db5deca7ca5f681ce656b3326be111b9ffe036d3 Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 9 May 2025 07:21:44 +0800 Subject: [PATCH 254/491] Fixed WebRTC build failure on iOS (#4423) --- aconfigure | 4 ++-- aconfigure.ac | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aconfigure b/aconfigure index d1753873ea..bbb2159137 100755 --- a/aconfigure +++ b/aconfigure @@ -11081,7 +11081,7 @@ printf "%s\n" "Checking if libwebrtc is disabled...no" >&6; } case $target in *-apple-darwin_ios*) case $target in - arm64*) + arm64* | aarch64*) ac_webrtc_instset=neon ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" ;; @@ -11234,7 +11234,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu case $target in *-apple-darwin_ios*) case $target in - arm64*) + arm64* | aarch64*) ac_webrtc_aec3_instset=neon ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" ;; diff --git a/aconfigure.ac b/aconfigure.ac index 1c21d432b3..2c73edcb08 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -2717,7 +2717,7 @@ AC_ARG_ENABLE(libwebrtc, case $target in *-apple-darwin_ios*) case $target in - arm64*) + arm64* | aarch64*) ac_webrtc_instset=neon ac_webrtc_cflags="-DWEBRTC_ARCH_ARM64" ;; @@ -2837,7 +2837,7 @@ AC_ARG_ENABLE(libwebrtc_aec3, case $target in *-apple-darwin_ios*) case $target in - arm64*) + arm64* | aarch64*) ac_webrtc_aec3_instset=neon ac_webrtc_aec3_cflags="-DWEBRTC_ARCH_ARM64" ;; From 5d9545935c9e4138835bd82e6be822862e087056 Mon Sep 17 00:00:00 2001 From: Yang sheng Date: Fri, 9 May 2025 14:41:47 +0800 Subject: [PATCH 255/491] Fix compile error with ffmpeg enable on linux (#4421) --- pjmedia/src/pjmedia-videodev/ffmpeg_dev.c | 13 ++++++++++--- pjsip-apps/src/vidgui/vidwin.h | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c b/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c index 491157f8eb..6804d38572 100644 --- a/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c +++ b/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c @@ -55,9 +55,11 @@ # include #endif -#define MAX_DEV_CNT 8 +#define MAX_DEV_CNT 8 +#define MAX_DEV_NAME_LEN 80 #ifndef PJMEDIA_USE_OLD_FFMPEG +# define av_free_packet av_packet_unref # define av_close_input_stream(ctx) avformat_close_input(&ctx) #endif @@ -303,8 +305,6 @@ static pj_status_t ffmpeg_factory_destroy(pjmedia_vid_dev_factory *f) # pragma warning(pop) #endif -#define MAX_DEV_NAME_LEN 80 - static pj_status_t dshow_enum_devices(unsigned *dev_cnt, char dev_names[][MAX_DEV_NAME_LEN]) { @@ -449,10 +449,17 @@ static pj_status_t ffmpeg_factory_refresh(pjmedia_vid_dev_factory *f) continue; for(i = 0; i < ctx->nb_streams; i++) { +#if LIBAVFORMAT_VER_AT_LEAST(59,0) + if (ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + codec = ctx->streams[i]->codecpar; + break; + } +#else if (ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { codec = ctx->streams[i]->codec; break; } +#endif } if (!codec) { av_close_input_stream(ctx); diff --git a/pjsip-apps/src/vidgui/vidwin.h b/pjsip-apps/src/vidgui/vidwin.h index abb221c2b8..dcd50ff104 100644 --- a/pjsip-apps/src/vidgui/vidwin.h +++ b/pjsip-apps/src/vidgui/vidwin.h @@ -29,7 +29,7 @@ class VidWin : public QWidget public: VidWin(const pjmedia_vid_dev_hwnd *hwnd, QWidget* parent = 0, - Qt::WindowFlags f = 0); + Qt::WindowFlags f = (Qt::WindowFlags)0); virtual ~VidWin(); QSize sizeHint() const { return size_hint; } From 0369f2c2fcb38be64252a56e81d5308e436112c2 Mon Sep 17 00:00:00 2001 From: Yang sheng Date: Thu, 15 May 2025 17:50:07 +0800 Subject: [PATCH 256/491] Fix compile error with ffmpeg master branch (#4425) --- pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c | 8 +++++++- pjmedia/src/pjmedia-videodev/ffmpeg_dev.c | 4 ++++ pjmedia/src/pjmedia/ffmpeg_util.h | 12 ++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c b/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c index 57b5c89b29..d861dd84e7 100644 --- a/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c +++ b/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c @@ -1967,8 +1967,14 @@ static pj_status_t ffmpeg_codec_decode_whole(pjmedia_vid_codec *codec, /* Check decoding result, e.g: see if the format got changed, * keyframe found/missing. */ +#if LIBAVUTIL_VER_AT_LEAST(58,7) status = check_decode_result(codec, &input->timestamp, - avframe.key_frame); + !!(avframe.flags & AV_FRAME_FLAG_KEY)); +#else + status = check_decode_result(codec, &input->timestamp, + avframe.key_frame); +#endif + /* avframe.key_frame); */ if (status != PJ_SUCCESS) { ffmpeg_frame_unref(&avframe); return status; diff --git a/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c b/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c index 6804d38572..b0098be262 100644 --- a/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c +++ b/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c @@ -411,7 +411,11 @@ static pj_status_t ffmpeg_factory_refresh(pjmedia_vid_dev_factory *f) unsigned dev_cnt = MAX_DEV_CNT; unsigned dev_idx; +#if LIBAVFORMAT_VER_AT_LEAST(60, 21) + if ((p->flags & AVFMT_NOFILE)==0) { +#else if ((p->flags & AVFMT_NOFILE)==0 || p->read_probe) { +#endif goto next_format; } diff --git a/pjmedia/src/pjmedia/ffmpeg_util.h b/pjmedia/src/pjmedia/ffmpeg_util.h index 6711f4847d..c6cff935e5 100644 --- a/pjmedia/src/pjmedia/ffmpeg_util.h +++ b/pjmedia/src/pjmedia/ffmpeg_util.h @@ -49,6 +49,18 @@ (LIBAVCODEC_VERSION_MAJOR == major && \ LIBAVCODEC_VERSION_MINOR >= minor)) +#define LIBAVFORMAT_VER_AT_LEAST(major,minor) (LIBAVFORMAT_VERSION_MAJOR > major || \ + (LIBAVFORMAT_VERSION_MAJOR == major && \ + LIBAVFORMAT_VERSION_MINOR >= minor)) + +#define LIBAVUTIL_VER_AT_LEAST(major,minor) (LIBAVUTIL_VERSION_MAJOR > major || \ + (LIBAVUTIL_VERSION_MAJOR == major && \ + LIBAVUTIL_VERSION_MINOR >= minor)) + +#if LIBAVCODEC_VER_AT_LEAST(60,39) +# define avcodec_close(x) ((void)0) +#endif + void pjmedia_ffmpeg_add_ref(); void pjmedia_ffmpeg_dec_ref(); From 2df1cb110da65a5c6385e197bc1001eeaf62f792 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Mon, 19 May 2025 07:38:08 +0200 Subject: [PATCH 257/491] Fix typo in pjsip_auth_create_aka_response docs (#4428) --- pjsip/include/pjsip/sip_auth_aka.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pjsip/include/pjsip/sip_auth_aka.h b/pjsip/include/pjsip/sip_auth_aka.h index 7f0ca8b6fd..803de9fd95 100644 --- a/pjsip/include/pjsip/sip_auth_aka.h +++ b/pjsip/include/pjsip/sip_auth_aka.h @@ -170,7 +170,7 @@ PJ_BEGIN_DECL * the credential. * * @param pool Pool to allocate memory. - * @param chal The authentication challenge sent by server in 401 + * @param chal The authentication challenge sent by server in 407 * or 401 response, as either Proxy-Authenticate or * WWW-Authenticate header. * @param cred The credential to be used. From 7414d47ae469b9bc1503a9b1958c6e130b91189c Mon Sep 17 00:00:00 2001 From: Faraz Khonsari Date: Tue, 20 May 2025 05:54:34 +0200 Subject: [PATCH 258/491] set VoiceCommunication as InputPreset for Oboe stream builder (#4432) --- pjmedia/src/pjmedia-audiodev/oboe_dev.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/pjmedia/src/pjmedia-audiodev/oboe_dev.cpp b/pjmedia/src/pjmedia-audiodev/oboe_dev.cpp index ce80141a12..10057bd5d4 100644 --- a/pjmedia/src/pjmedia-audiodev/oboe_dev.cpp +++ b/pjmedia/src/pjmedia-audiodev/oboe_dev.cpp @@ -583,6 +583,7 @@ class MyOboeEngine : oboe::AudioStreamDataCallback, sb.setPerformanceMode(oboe::PerformanceMode::LowLatency); sb.setFormat(oboe::AudioFormat::I16); sb.setUsage(oboe::Usage::VoiceCommunication); + sb.setInputPreset(oboe::InputPreset::VoiceCommunication); sb.setContentType(oboe::ContentType::Speech); sb.setDataCallback(this); sb.setErrorCallback(this); From 1b5295c574dd522bb876bd2e3f291e0b66377441 Mon Sep 17 00:00:00 2001 From: Weslley Luiz <37993565+weslleymurdock@users.noreply.github.com> Date: Tue, 20 May 2025 01:26:32 -0300 Subject: [PATCH 259/491] Add MAUI example app to csharp samples (#4078) --- .gitignore | 2 + pjsip-apps/src/swig/csharp/Makefile | 40 +- .../src/swig/csharp/pjsua2maui/.gitignore | 6 + .../src/swig/csharp/pjsua2maui/README.md | 147 +++++++ .../src/swig/csharp/pjsua2maui/pjsua2maui.sln | 23 + .../csharp/pjsua2maui/pjsua2maui/.gitignore | 6 + .../csharp/pjsua2maui/pjsua2maui/App.xaml | 14 + .../csharp/pjsua2maui/pjsua2maui/App.xaml.cs | 11 + .../pjsua2maui/pjsua2maui/AppShell.xaml | 14 + .../pjsua2maui/pjsua2maui/AppShell.xaml.cs | 9 + .../pjsua2maui/Controls/CallView.cs | 8 + .../pjsua2maui/pjsua2maui/MauiProgram.cs | 40 ++ .../pjsua2maui/Messages/AddBuddyMessage.cs | 11 + .../pjsua2maui/Messages/EditBuddyMessage.cs | 13 + .../Messages/SaveAccountConfigMessage.cs | 11 + .../Messages/UpdateCallStateMessage.cs | 14 + .../Messages/UpdateMediaCallStateMessage.cs | 14 + .../pjsua2maui/Models/ISoftObserver.cs | 14 + .../pjsua2maui/Models/SoftAccount.cs | 78 ++++ .../pjsua2maui/Models/SoftAccountConfig.cs | 51 +++ .../Models/SoftAccountConfigModel.cs | 36 ++ .../pjsua2maui/pjsua2maui/Models/SoftApp.cs | 249 +++++++++++ .../pjsua2maui/pjsua2maui/Models/SoftBuddy.cs | 56 +++ .../pjsua2maui/pjsua2maui/Models/SoftCall.cs | 88 ++++ .../pjsua2maui/Models/SoftLogWriter.cs | 11 + .../Platforms/Android/AndroidManifest.xml | 13 + .../Platforms/Android/CallPageHandler.cs | 268 ++++++++++++ .../Platforms/Android/MainActivity.cs | 10 + .../Platforms/Android/MainApplication.cs | 35 ++ .../Resources/layout/activity_call.xml | 46 ++ .../Android/Resources/values/colors.xml | 7 + .../pjsua2maui/Platforms/iOS/AppDelegate.cs | 9 + .../Platforms/iOS/CallPageHandler.cs | 286 +++++++++++++ .../pjsua2maui/Platforms/iOS}/Info.plist | 10 +- .../pjsua2maui/Platforms/iOS/Program.cs | 15 + .../pjsua2maui/Properties/launchSettings.json | 8 + .../pjsua2maui/Resources/AppIcon/appicon.svg | 4 + .../Resources/AppIcon/appiconfg.svg | 8 + .../Resources/Fonts/OpenSans-Regular.ttf | Bin 0 -> 107280 bytes .../Resources/Fonts/OpenSans-Semibold.ttf | Bin 0 -> 111164 bytes .../Resources/Images/dotnet_bot.svg | 93 ++++ .../pjsua2maui/Resources/Raw/AboutAssets.txt | 15 + .../pjsua2maui/Resources/Splash/splash.svg | 8 + .../pjsua2maui/Resources/Styles/Colors.xaml | 44 ++ .../pjsua2maui/Resources/Styles/Styles.xaml | 405 ++++++++++++++++++ .../Resources/layout/activity_call.xml | 51 +++ .../ViewModels/AVConfigViewModel.cs | 19 + .../ViewModels/AccountConfigViewModel.cs | 19 + .../pjsua2maui/ViewModels/BaseViewModel.cs | 46 ++ .../ViewModels/BuddyConfigViewModel.cs | 21 + .../pjsua2maui/ViewModels/BuddyViewModel.cs | 41 ++ .../pjsua2maui/Views/AccountConfigPage.xaml | 42 ++ .../Views/AccountConfigPage.xaml.cs | 34 ++ .../pjsua2maui/Views/BuddyConfigPage.xaml | 40 ++ .../pjsua2maui/Views/BuddyConfigPage.xaml.cs | 48 +++ .../pjsua2maui/Views/BuddyPage.xaml | 59 +++ .../pjsua2maui/Views/BuddyPage.xaml.cs | 250 +++++++++++ .../pjsua2maui/pjsua2maui/Views/CallPage.xaml | 13 + .../pjsua2maui/Views/CallPage.xaml.cs | 10 + .../pjsua2maui/pjsua2maui/pjsua2maui.csproj | 127 ++++++ .../pjsua2maui/pjsua2maui.csproj.user | 10 + 61 files changed, 3073 insertions(+), 7 deletions(-) create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/.gitignore create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/README.md create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui.sln create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/.gitignore create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/App.xaml create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/App.xaml.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/AppShell.xaml create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/AppShell.xaml.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Controls/CallView.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/MauiProgram.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/AddBuddyMessage.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/EditBuddyMessage.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/SaveAccountConfigMessage.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/UpdateCallStateMessage.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/UpdateMediaCallStateMessage.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/ISoftObserver.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftAccount.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftAccountConfig.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftAccountConfigModel.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftApp.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftBuddy.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftCall.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftLogWriter.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/AndroidManifest.xml create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/CallPageHandler.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/MainActivity.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/MainApplication.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/Resources/layout/activity_call.xml create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/Resources/values/colors.xml create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/iOS/AppDelegate.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/iOS/CallPageHandler.cs rename pjsip-apps/src/swig/csharp/{pjsua2xamarin/pjsua2xamarin.iOS => pjsua2maui/pjsua2maui/Platforms/iOS}/Info.plist (87%) create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/iOS/Program.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Properties/launchSettings.json create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Resources/AppIcon/appicon.svg create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Resources/AppIcon/appiconfg.svg create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Resources/Fonts/OpenSans-Regular.ttf create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Resources/Fonts/OpenSans-Semibold.ttf create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Resources/Images/dotnet_bot.svg create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Resources/Raw/AboutAssets.txt create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Resources/Splash/splash.svg create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Resources/Styles/Colors.xaml create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Resources/Styles/Styles.xaml create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Resources/layout/activity_call.xml create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/ViewModels/AVConfigViewModel.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/ViewModels/AccountConfigViewModel.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/ViewModels/BaseViewModel.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/ViewModels/BuddyConfigViewModel.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/ViewModels/BuddyViewModel.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Views/AccountConfigPage.xaml create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Views/AccountConfigPage.xaml.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Views/BuddyConfigPage.xaml create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Views/BuddyConfigPage.xaml.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Views/BuddyPage.xaml create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Views/BuddyPage.xaml.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Views/CallPage.xaml create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Views/CallPage.xaml.cs create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/pjsua2maui.csproj create mode 100644 pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/pjsua2maui.csproj.user diff --git a/.gitignore b/.gitignore index 362b9c8902..84cdbc1ff7 100644 --- a/.gitignore +++ b/.gitignore @@ -87,3 +87,5 @@ tests/fuzz/*.o cov-int/ getversion.mak configure.out +*/Platforms/iOS/pjsua2/* +*/Platforms/iOS/lib/* diff --git a/pjsip-apps/src/swig/csharp/Makefile b/pjsip-apps/src/swig/csharp/Makefile index 8be9d6d5da..ed3a10aaa4 100644 --- a/pjsip-apps/src/swig/csharp/Makefile +++ b/pjsip-apps/src/swig/csharp/Makefile @@ -21,6 +21,8 @@ SWIG_FLAGS += -w312 PROJ_NAME=pjsua2xamarin OUT_DIR=$(PROJ_NAME)/$(PROJ_NAME)/pjsua2 NAMESPACE=$(PROJ_NAME).pjsua2 +MAUI_PROJ_NAME=pjsua2maui +MAUI_NAMESPACE=libpjsua2.maui ARCH=$(TARGET_ARCH) @@ -28,14 +30,22 @@ ifeq ($(OS),android) LIBPJSUA2_DIR=$(PROJ_NAME)/$(PROJ_NAME).Android/lib/$(ARCH) LIBPJSUA2=$(LIBPJSUA2_DIR)/libpjsua2.so SUP_CLASS_DIR=$(PROJ_NAME)/$(PROJ_NAME).Android/org/pjsip + MAUI_LIBPJSUA2_DIR=$(MAUI_PROJ_NAME)/$(MAUI_PROJ_NAME)/Platforms/Android/lib/$(ARCH) + MAUI_LIBPJSUA2=$(MAUI_LIBPJSUA2_DIR)/libpjsua2.so + MAUI_SUP_CLASS_DIR=$(MAUI_PROJ_NAME)/$(MAUI_PROJ_NAME)/Platforms/Android/org/pjsip + MAUI_OUT_DIR=$(MAUI_PROJ_NAME)/$(MAUI_PROJ_NAME)/Platforms/Android/pjsua2 else ifeq ($(OS),ios) LIBPJSUA2_DIR=$(PROJ_NAME)/$(PROJ_NAME).iOS/lib/$(ARCH) + MAUI_LIBPJSUA2_DIR=$(MAUI_PROJ_NAME)/$(MAUI_PROJ_NAME)/Platforms/iOS/lib/$(ARCH) SWIG_FLAGS += -dllimport "__Internal" + MAUI_OUT_DIR=$(MAUI_PROJ_NAME)/$(MAUI_PROJ_NAME)/Platforms/iOS/pjsua2 else LIBPJSUA2_DIR=$(PROJ_NAME)/lib + endif LIBPJSUA2=$(LIBPJSUA2_DIR)/libpjsua2.a + MAUI_LIBPJSUA2=$(MAUI_LIBPJSUA2_DIR)/libpjsua2.a endif # Build settings @@ -44,7 +54,7 @@ MY_LDFLAGS := $(PJ_LDXXFLAGS) $(PJ_LDXXLIBS) $(LDFLAGS) .PHONY: all install uninstall -all: $(LIBPJSUA2) sample +all: $(LIBPJSUA2) $(MAUI_LIBPJSUA2) sample $(LIBPJSUA2): $(OUT_DIR)/pjsua2_wrap.o mkdir -p $(LIBPJSUA2_DIR) @@ -74,6 +84,34 @@ $(OUT_DIR)/pjsua2_wrap.cpp: ../pjsua2.i ../symbols.i Makefile $(SRCS) mkdir -p $(OUT_DIR) swig $(SWIG_FLAGS) -namespace $(NAMESPACE) -csharp -o $(OUT_DIR)/pjsua2_wrap.cpp ../pjsua2.i +$(MAUI_LIBPJSUA2): $(MAUI_OUT_DIR)/pjsua2_wrap.o + mkdir -p $(MAUI_LIBPJSUA2_DIR) +ifeq ($(OS),android) + mkdir -p $(MAUI_SUP_CLASS_DIR) + $(PJ_CXX) -shared -o $(MAUI_LIBPJSUA2) $(MAUI_OUT_DIR)/pjsua2_wrap.o \ + $(MY_CFLAGS) $(MY_LDFLAGS) + # copy libc++_shared.so manually + cp -f ${STD_CPP_LIB} $(MAUI_LIBPJSUA2_DIR) +else + $(AR) $(MAUI_LIBPJSUA2) $(AR_FLAGS) $(MAUI_OUT_DIR)/pjsua2_wrap.o $(PJ_LIBXX_FILES) +endif +ifneq (,$(findstring PJMEDIA_VIDEO_DEV_HAS_ANDROID=1,$(ANDROID_CFLAGS))) + @echo "Copying Android camera helper components..." + cp $(PJDIR)/pjmedia/src/pjmedia-videodev/android/PjCamera*.java $(MAUI_SUP_CLASS_DIR)/ +endif +ifneq (,$(findstring PJMEDIA_AUDIO_DEV_HAS_OBOE=1,$(OBOE_CFLAGS))) + @echo "Copying Android Oboe audio device helper components..." + cp $(PJDIR)/pjmedia/src/pjmedia-audiodev/android/PjAudioDevInfo.java $(MAUI_SUP_CLASS_DIR)/ +endif + +$(MAUI_OUT_DIR)/pjsua2_wrap.o: $(MAUI_OUT_DIR)/pjsua2_wrap.cpp + $(PJ_CXX) -c $(MAUI_OUT_DIR)/pjsua2_wrap.cpp -o $(MAUI_OUT_DIR)/pjsua2_wrap.o \ + $(MY_CFLAGS) + +$(MAUI_OUT_DIR)/pjsua2_wrap.cpp: ../pjsua2.i ../symbols.i Makefile $(SRCS) + mkdir -p $(MAUI_OUT_DIR) + swig $(SWIG_FLAGS) -namespace $(MAUI_NAMESPACE) -csharp -o $(MAUI_OUT_DIR)/pjsua2_wrap.cpp ../pjsua2.i + sample: sample.cs @echo "Copying sample code..." cp sample.cs $(PROJ_NAME)/$(PROJ_NAME) diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/.gitignore b/pjsip-apps/src/swig/csharp/pjsua2maui/.gitignore new file mode 100644 index 0000000000..458dbd8723 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/.gitignore @@ -0,0 +1,6 @@ +.dll +.java +.class +.jar +.so +/*/obj/ diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/README.md b/pjsip-apps/src/swig/csharp/pjsua2maui/README.md new file mode 100644 index 0000000000..f1a28b28b6 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/README.md @@ -0,0 +1,147 @@ +# pjsua2maui - pjsip on .net maui + +## STEPS FOR BUILDING: + +### Android + +- SETUP: + - Visual Studio Code & .NET MAUI - [Install steps](https://learn.microsoft.com/en-us/dotnet/maui/get-started/installation?view=net-maui-11.0&tabs=visual-studio-code) + - Java 17 + - Android NDK r21d or later + - pjsip v2.14.1 or later + +- Run the following commands: + +```bash + export ANDROID_SDK_ROOT=/somewhere/there/is/installed/the/sdk/ + sdkmanager --install "ndk;28.0.12674087" --channel=3 --sdk_root=$ANDROID_SDK_ROOT + sdkmanager --install "cmake;3.31.0" --sdk_root=$ANDROID_SDK_ROOT + sdkmanager --install "build-tools;35.0.0" "platform-tools" "platforms;android-35" --sdk_root=$ANDROID_SDK_ROOT + echo yes | sdkmanager --licenses --sdk_root=$ANDROID_SDK_ROOT +``` +note: +If you are sharing the Android SDK location with Android Studio, the recommended way to install/update the SDK and accept licenses is by using Android Studio. + +- Create the following variable: + +```bash + +export ANDROID_NDK_ROOT=/path/to/ndk/root/installation + +``` + +- Then build pjsip + +For device: +```bash + ./configure-android +``` + +For simulator: +```bash + +TARGET_ABI=x86_64 ./configure-android +make dep && make clean && make + +``` + + +after run make dep and make, run the make on ``` pjsip-apps/src/swig/csharp ``` folder + +```bash + +make + +``` + +then start the android emulator, go to ``` pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui ``` and run: + +```bash + +dotnet workload restore +dotnet restore +dotnet build -t:Run -c Debug -f net9.0-android + +``` +or, for run in device: + +Open Android Studio and connect an Android device, such as by using "Pair Devices Using Wifi" +(Optional) Start Logcat in VS Code, for obtaining Android log. +Install Logcat: View-Extensions. Install Logcat extension. +Start Logcat: View-Command Pallette. + +### iOS + +- SETUP: + - Visual Studio Code & .NET MAUI - [Install steps](https://learn.microsoft.com/en-us/dotnet/maui/get-started/installation?view=net-maui-11.0&tabs=visual-studio-code) + - Xcode 16.1 or later + - pjsip v2.14.1 or later + +- PREPARING: + +- Building pj stack: + +Create the following ``` config_site.h ``` : + +```c + +#define PJ_CONFIG_IPHONE 1 + +#include + +``` +Create the following variable: + +```bash + +export DEVPATH="`xcrun -sdk iphonesimulator --show-sdk-platform-path`/Developer" + +``` +Run configure with the following flags to build the lib to run on simulator: + +```bash + +MIN_IOS="-miphoneos-version-min=12.2" ARCH="-arch x86_64" CFLAGS="-O2 -m32 -mios-simulator-version-min=12.2 -fembed-bitcode" LDFLAGS="-O2 -m32 -mios-simulator-version-min=12.2 -fembed-bitcode" ./configure-iphone + +``` +For run on device: + +```bash +./configure-iphone +``` + +after run make dep and make, run the make on ``` pjsip-apps/src/swig/csharp ``` folder + +```bash + +make + +``` + +then go to ``` pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui ``` and run: + +For simulator: + +```bash +dotnet workload restore +dotnet restore +dotnet build -t:Run -c Debug -f net9.0-ios -p:_DeviceName=:v2:udid=UDID_OF_YOUR_EMULATOR +``` + +For device: + +```bash +dotnet workload restore +dotnet restore +dotnet build -t:Run -c Debug -f net9.0-ios -p:_DeviceName=:v2:udid=UDID_OF_YOUR_DEVICE +``` + +For the codesign, here are the steps: + +```bash +dotnet publish -c Debug -f net9.0-ios -p:RuntimeIdentifier=ios-arm64 +``` +Open the resulting .ipa file in Xcode: ``` pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/bin/Debug/net9.0-ios/ios-arm64/publish/pjsua2maui.ipa ``` + +Drag it to your connected device under "Devices and Simulators." +Xcode will handle the signing and deployment. diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui.sln b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui.sln new file mode 100644 index 0000000000..36819af193 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui.sln @@ -0,0 +1,23 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "pjsua2maui", "pjsua2maui\pjsua2maui.csproj", "{C9D0190E-58BC-417B-9810-335D234D5002}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C9D0190E-58BC-417B-9810-335D234D5002}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C9D0190E-58BC-417B-9810-335D234D5002}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C9D0190E-58BC-417B-9810-335D234D5002}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {C9D0190E-58BC-417B-9810-335D234D5002}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C9D0190E-58BC-417B-9810-335D234D5002}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/.gitignore b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/.gitignore new file mode 100644 index 0000000000..458dbd8723 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/.gitignore @@ -0,0 +1,6 @@ +.dll +.java +.class +.jar +.so +/*/obj/ diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/App.xaml b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/App.xaml new file mode 100644 index 0000000000..04dd79d146 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/App.xaml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/App.xaml.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/App.xaml.cs new file mode 100644 index 0000000000..dc5fd0cd3f --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/App.xaml.cs @@ -0,0 +1,11 @@ +namespace pjsua2maui; + +public partial class App : Application +{ + public App() + { + InitializeComponent(); + + MainPage = new AppShell(); + } +} diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/AppShell.xaml b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/AppShell.xaml new file mode 100644 index 0000000000..d4151744c2 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/AppShell.xaml @@ -0,0 +1,14 @@ + + + + + + diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/AppShell.xaml.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/AppShell.xaml.cs new file mode 100644 index 0000000000..35c63330f1 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/AppShell.xaml.cs @@ -0,0 +1,9 @@ +namespace pjsua2maui; + +public partial class AppShell : Shell +{ + public AppShell() + { + InitializeComponent(); + } +} diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Controls/CallView.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Controls/CallView.cs new file mode 100644 index 0000000000..5508ad903d --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Controls/CallView.cs @@ -0,0 +1,8 @@ +namespace pjsua2maui.Controls; + +public class CallView : ContentView +{ + public CallView() + { + } +} diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/MauiProgram.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/MauiProgram.cs new file mode 100644 index 0000000000..f26251db9b --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/MauiProgram.cs @@ -0,0 +1,40 @@ +using Microsoft.Extensions.Logging; +using pjsua2maui.Controls; +#if ANDROID +using pjsua2maui.Platforms.Android; +#endif +#if IOS +using pjsua2maui.Platforms.iOS; +#endif + +namespace pjsua2maui; + +public static class MauiProgram +{ + public static MauiApp CreateMauiApp() + { + var builder = MauiApp.CreateBuilder(); + builder + .UseMauiApp() + .ConfigureFonts(fonts => + { + fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); + fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); + }); + +#if DEBUG + builder.Logging.AddDebug(); +#endif +#if __ANDROID__ + builder.ConfigureMauiHandlers((x) => { + x.AddHandler(typeof(CallView), typeof(CallPageRenderer)); + }); +#endif +#if __IOS__ + builder.ConfigureMauiHandlers((x) => { + x.AddHandler(typeof(CallView), typeof(CallPageRenderer)); + }); +#endif + return builder.Build(); + } +} diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/AddBuddyMessage.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/AddBuddyMessage.cs new file mode 100644 index 0000000000..0455f062b8 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/AddBuddyMessage.cs @@ -0,0 +1,11 @@ +using CommunityToolkit.Mvvm.Messaging.Messages; +using libpjsua2.maui; + +namespace pjsua2maui.Messages; + +public class AddBuddyMessage : ValueChangedMessage +{ + public AddBuddyMessage(BuddyConfig buddyConfig) : base(buddyConfig) + { + } +} diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/EditBuddyMessage.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/EditBuddyMessage.cs new file mode 100644 index 0000000000..5c888b3fb1 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/EditBuddyMessage.cs @@ -0,0 +1,13 @@ +using System; +using CommunityToolkit.Mvvm.Messaging.Messages; +using libpjsua2.maui; + +namespace pjsua2maui.Messages; + +public class EditBuddyMessage : ValueChangedMessage +{ + public EditBuddyMessage(BuddyConfig buddyConfig) : base(buddyConfig) + { + } +} + diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/SaveAccountConfigMessage.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/SaveAccountConfigMessage.cs new file mode 100644 index 0000000000..7a13bdb2be --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/SaveAccountConfigMessage.cs @@ -0,0 +1,11 @@ +using CommunityToolkit.Mvvm.Messaging.Messages; +using pjsua2maui.Models; + +namespace pjsua2maui.Messages; + +public class SaveAccountConfigMessage : ValueChangedMessage +{ + public SaveAccountConfigMessage(SoftAccountConfigModel softAccountConfigModel) : base(softAccountConfigModel) + { + } +} diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/UpdateCallStateMessage.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/UpdateCallStateMessage.cs new file mode 100644 index 0000000000..d791053857 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/UpdateCallStateMessage.cs @@ -0,0 +1,14 @@ +using System; +using CommunityToolkit.Mvvm.Messaging.Messages; +using libpjsua2.maui; + +namespace pjsua2maui.Messages; + +public class UpdateCallStateMessage : ValueChangedMessage +{ + public UpdateCallStateMessage(CallInfo callInfo) : base(callInfo) + { + } +} + + diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/UpdateMediaCallStateMessage.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/UpdateMediaCallStateMessage.cs new file mode 100644 index 0000000000..786d09c0f5 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Messages/UpdateMediaCallStateMessage.cs @@ -0,0 +1,14 @@ +using System; +using CommunityToolkit.Mvvm.Messaging.Messages; +using libpjsua2.maui; + +namespace pjsua2maui.Messages; + +public class UpdateMediaCallStateMessage : ValueChangedMessage +{ + public UpdateMediaCallStateMessage(CallInfo callInfo) : base(callInfo) + { + } +} + + diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/ISoftObserver.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/ISoftObserver.cs new file mode 100644 index 0000000000..ea478cc35f --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/ISoftObserver.cs @@ -0,0 +1,14 @@ +using System; +namespace pjsua2maui.Models; + +/* Interface to separate UI & engine a bit better */ +public interface ISoftObserver +{ + void notifyRegState(int code, String reason, long expiration); + void notifyIncomingCall(SoftCall call); + void notifyCallState(SoftCall call); + void notifyCallMediaState(SoftCall call); + void notifyBuddyState(SoftBuddy buddy); + void notifyChangeNetwork(); +} + diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftAccount.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftAccount.cs new file mode 100644 index 0000000000..3ac2a98f53 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftAccount.cs @@ -0,0 +1,78 @@ +using libpjsua2.maui; + +namespace pjsua2maui.Models; + +public class SoftAccount : Account +{ + public List buddyList = new List(); + public AccountConfig cfg; + + ~SoftAccount() + { + Console.WriteLine("*** Account is being deleted"); + } + + public SoftAccount(AccountConfig config) + { + cfg = config; + } + + public SoftBuddy addBuddy(BuddyConfig bud_cfg) + { + /* Create Buddy */ + SoftBuddy bud = new SoftBuddy(bud_cfg); + try + { + bud.create(this, bud_cfg); + } + catch (Exception) + { + bud.Dispose(); + } + + if (bud != null) + { + buddyList.Add(bud); + if (bud_cfg.subscribe) + try + { + bud.subscribePresence(true); + } + catch (Exception) { } + } + + return bud; + } + + public void delBuddy(SoftBuddy buddy) + { + buddyList.Remove(buddy); + buddy.Dispose(); + } + + override public void onRegState(OnRegStateParam prm) + { + AccountInfo ai = getInfo(); + Console.WriteLine("***" + (ai.regIsActive ? "" : "Un") + + "Register: code=" + prm.code); + + SoftApp.observer.notifyRegState((int)prm.code, prm.reason, prm.expiration); + } + + override public void onIncomingCall(OnIncomingCallParam prm) + { + Console.WriteLine("======== Incoming call ======== "); + SoftCall call = new SoftCall(this, prm.callId); + SoftApp.observer.notifyIncomingCall(call); + } + + override public void onInstantMessage(OnInstantMessageParam prm) + { + Console.WriteLine("======== Incoming pager ======== "); + Console.WriteLine("From : " + prm.fromUri); + Console.WriteLine("To : " + prm.toUri); + Console.WriteLine("Contact : " + prm.contactUri); + Console.WriteLine("Mimetype : " + prm.contentType); + Console.WriteLine("Body : " + prm.msgBody); + } +} \ No newline at end of file diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftAccountConfig.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftAccountConfig.cs new file mode 100644 index 0000000000..d88f3a579a --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftAccountConfig.cs @@ -0,0 +1,51 @@ +using libpjsua2.maui; + +namespace pjsua2maui.Models; + +public class SoftAccountConfig +{ + public AccountConfig accCfg; + public List buddyCfgs = new List(); + + public SoftAccountConfig() + { + accCfg = new AccountConfig(); + } + + public void readObject(ContainerNode accNode) + { + try + { + accCfg.readObject(accNode); + ContainerNode buddiesNode = accNode.readArray("Buddies"); + buddyCfgs.Clear(); + while (buddiesNode.hasUnread()) + { + BuddyConfig budCfg = new BuddyConfig(); + budCfg.readObject(buddiesNode); + buddyCfgs.Add(budCfg); + } + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + } + + public void writeObject(ContainerNode accNode) + { + try + { + accCfg.writeObject(accNode); + ContainerNode buddiesNode = accNode.writeNewArray("Buddies"); + foreach (BuddyConfig budCfg in buddyCfgs) + { + budCfg.writeObject(buddiesNode); + } + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + } +} \ No newline at end of file diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftAccountConfigModel.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftAccountConfigModel.cs new file mode 100644 index 0000000000..f06572fd1b --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftAccountConfigModel.cs @@ -0,0 +1,36 @@ +using libpjsua2.maui; + +namespace pjsua2maui.Models +{ + public class SoftAccountConfigModel + { + public string idUri { get; set; } + public string registrarUri { get; set; } + public string proxy { get; set; } + public string username { get; set; } + public string password { get; set; } + private SoftAccountConfig accConfig; + + public SoftAccountConfigModel(SoftAccountConfig inAccConfig) + { + accConfig = inAccConfig; + AccountConfig accCfg = accConfig.accCfg; + + idUri = accCfg.idUri; + registrarUri = accCfg.regConfig.registrarUri; + if (accCfg.sipConfig.proxies.Count > 0) + proxy = accCfg.sipConfig.proxies[0]; + else + proxy = ""; + + if (accCfg.sipConfig.authCreds.Count > 0) { + username = accCfg.sipConfig.authCreds[0].username; + password = accCfg.sipConfig.authCreds[0].data; + } else { + username = ""; + password = ""; + } + } + } +} + diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftApp.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftApp.cs new file mode 100644 index 0000000000..3eb43b971f --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftApp.cs @@ -0,0 +1,249 @@ +using libpjsua2.maui; + +namespace pjsua2maui.Models; + +public class SoftApp +{ + public static Endpoint ep = new Endpoint(); + public static SoftCall currentCall = null; + public static SoftAccount account = null; + public static SoftAccountConfig myAccCfg; + public static ISoftObserver observer; + + private static SoftLogWriter logWriter = new SoftLogWriter(); + private EpConfig epConfig = new EpConfig(); + private TransportConfig sipTpConfig = new TransportConfig(); + private String appDir; + + private const String configName = "Softhand.json"; + private const int SIP_PORT = 6000; + private const int LOG_LEVEL = 5; + + public SoftApp() + { + + } + + public void init(ISoftObserver obs, String app_dir) + { + observer = obs; + appDir = app_dir; + + /* Create endpoint */ + try + { + Dispatcher.GetForCurrentThread().Dispatch(ep.libCreate); + } + catch (Exception e) + { + Console.WriteLine("Error init : " + e.Message); + return; + } + + myAccCfg = new SoftAccountConfig(); + /* Load config */ + String configPath = appDir + "/" + configName; + if (File.Exists(configPath)) + { + Dispatcher.GetForCurrentThread().Dispatch(() => loadConfig(configPath)); + } + else + { + /* Set 'default' values */ + sipTpConfig.port = SIP_PORT; + } + + /* Override log level setting */ + epConfig.logConfig.level = LOG_LEVEL; + epConfig.logConfig.consoleLevel = LOG_LEVEL; + + /* Set log config. */ + LogConfig log_cfg = epConfig.logConfig; + logWriter = new SoftLogWriter(); + log_cfg.writer = logWriter; + log_cfg.decor += (uint)pj_log_decoration.PJ_LOG_HAS_NEWLINE; + + UaConfig ua_cfg = epConfig.uaConfig; + ua_cfg.userAgent = "Softhand " + ep.libVersion().full; + + /* Init endpoint */ + try + { + Dispatcher.GetForCurrentThread().Dispatch(() => ep.libInit(epConfig)); + } + catch (Exception) + { + return; + } + + /* Create transports. */ + try + { + Dispatcher.GetForCurrentThread().Dispatch(() => ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_UDP, + sipTpConfig)); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + + try + { + Dispatcher.GetForCurrentThread().Dispatch(() => ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_TCP, + sipTpConfig)); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + + //try + //{ + // sipTpConfig.port = SIP_PORT + 1; + // Dispatcher.GetForCurrentThread().Dispatch(() => ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_TLS, + // sipTpConfig)); + //} + //catch (Exception e) + //{ + // Console.WriteLine(e.Message); + //} + + /* Set SIP port back to default for JSON saved config */ + sipTpConfig.port = SIP_PORT; + AccountConfig accountConfig = myAccCfg.accCfg; + if (accountConfig.idUri == "") + { + accountConfig.idUri = "sip:localhost"; + } + accountConfig.natConfig.iceEnabled = true; + accountConfig.videoConfig.autoTransmitOutgoing = true; + accountConfig.videoConfig.autoShowIncoming = true; + accountConfig.mediaConfig.srtpUse = pjmedia_srtp_use.PJMEDIA_SRTP_OPTIONAL; + accountConfig.mediaConfig.srtpSecureSignaling = 0; + + account = new SoftAccount(accountConfig); + try + { + Dispatcher.GetForCurrentThread().Dispatch(() => { + account.create(accountConfig); + + /* Add Buddies */ + foreach (BuddyConfig budCfg in myAccCfg.buddyCfgs) + { + account.addBuddy(budCfg); + } + }); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + account = null; + } + + /* Start. */ + try + { + Dispatcher.GetForCurrentThread().Dispatch(ep.libStart); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + } + + public void deinit() + { + String configPath = appDir + "/" + configName; + saveConfig(configPath); + + /* Shutdown pjsua. Note that Endpoint destructor will also invoke + * libDestroy(), so this will be a test of double libDestroy(). + */ + try + { + ep.libDestroy(); + } + catch (Exception) { } + + /* Force delete Endpoint here, to avoid deletion from a non- + * registered thread (by GC?). + */ + ep.Dispose(); + ep = null; + } + + private void loadConfig(String filename) + { + JsonDocument json = new JsonDocument(); + try + { + /* Load file */ + json.loadFile(filename); + ContainerNode root = json.getRootContainer(); + + /* Read endpoint config */ + epConfig.readObject(root); + + /* Read transport config */ + ContainerNode tpNode = root.readContainer("SipTransport"); + sipTpConfig.readObject(tpNode); + + /* Read account config */ + ContainerNode accNode = root.readContainer("SoftAccountConfig"); + myAccCfg.readObject(accNode); + + /* Force delete json now */ + json.Dispose(); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + } + + private void buildAccConfigs() + { + SoftAccountConfig tmpAccCfg = new SoftAccountConfig(); + tmpAccCfg.accCfg = account.cfg; + + tmpAccCfg.buddyCfgs.Clear(); + for (int j = 0; j < account.buddyList.Count; j++) + { + SoftBuddy bud = (SoftBuddy)account.buddyList[j]; + tmpAccCfg.buddyCfgs.Add(bud.cfg); + } + + myAccCfg = tmpAccCfg; + } + + private void saveConfig(String filename) + { + try + { + JsonDocument json = new JsonDocument(); + + /* Write endpoint config */ + json.writeObject(epConfig); + + /* Write transport config */ + ContainerNode tpNode = json.writeNewContainer("SipTransport"); + sipTpConfig.writeObject(tpNode); + + /* Write account configs */ + buildAccConfigs(); + ContainerNode accNode = json.writeNewContainer("SoftAccountConfig"); + myAccCfg.writeObject(accNode); + + /* Save file */ + json.saveFile(filename); + + /* Force delete json now */ + json.Dispose(); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + } +} + diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftBuddy.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftBuddy.cs new file mode 100644 index 0000000000..1494a47dd5 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftBuddy.cs @@ -0,0 +1,56 @@ +using libpjsua2.maui; + +namespace pjsua2maui.Models; + +public class SoftBuddy : Buddy +{ + public BuddyConfig cfg { get; set; } + + public SoftBuddy(BuddyConfig config) + { + cfg = config; + } + + public String getStatusText() + { + BuddyInfo bi; + + try + { + bi = getInfo(); + } + catch (Exception) + { + return "?"; + } + + String status = ""; + if (bi.subState == pjsip_evsub_state.PJSIP_EVSUB_STATE_ACTIVE) + { + if (bi.presStatus.status == + pjsua_buddy_status.PJSUA_BUDDY_STATUS_ONLINE) + { + status = bi.presStatus.statusText; + if (status == null || status.Length == 0) + { + status = "Online"; + } + } + else if (bi.presStatus.status == + pjsua_buddy_status.PJSUA_BUDDY_STATUS_OFFLINE) + { + status = "Offline"; + } + else + { + status = "Unknown"; + } + } + return status; + } + + override public void onBuddyState() + { + SoftApp.observer.notifyBuddyState(this); + } +} \ No newline at end of file diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftCall.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftCall.cs new file mode 100644 index 0000000000..c70bcf5134 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftCall.cs @@ -0,0 +1,88 @@ +using libpjsua2.maui; + +namespace pjsua2maui.Models +{ + public class SoftCall : Call + { + public VideoWindow vidWin; + public VideoPreview vidPrev; + + public SoftCall(SoftAccount acc, int call_id) : base(acc, call_id) + { + vidWin = null; + vidPrev = null; + } + + override public void onCallState(OnCallStateParam prm) + { + try + { + CallInfo ci = getInfo(); + if (ci.state == + pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED) + { + SoftApp.ep.utilLogWrite(3, "SoftCall", this.dump(true, "")); + } + } + catch (Exception ex) + { + Console.WriteLine("Error : " + ex.Message); + } + + // Should not delete this call instance (self) in this context, + // so the observer should manage this call instance deletion + // out of this callback context. + SoftApp.observer.notifyCallState(this); + } + + override public void onCallMediaState(OnCallMediaStateParam prm) + { + CallInfo ci; + try + { + ci = getInfo(); + } + catch (Exception) + { + return; + } + + CallMediaInfoVector cmiv = ci.media; + + for (int i = 0; i < cmiv.Count; i++) + { + CallMediaInfo cmi = cmiv[i]; + if (cmi.type == pjmedia_type.PJMEDIA_TYPE_AUDIO && + (cmi.status == + pjsua_call_media_status.PJSUA_CALL_MEDIA_ACTIVE || + cmi.status == + pjsua_call_media_status.PJSUA_CALL_MEDIA_REMOTE_HOLD)) + { + // connect ports + try + { + AudDevManager audMgr = SoftApp.ep.audDevManager(); + AudioMedia am = getAudioMedia(i); + audMgr.getCaptureDevMedia().startTransmit(am); + am.startTransmit(audMgr.getPlaybackDevMedia()); + } + catch (Exception e) + { + Console.WriteLine("Failed connecting media ports" + + e.Message); + continue; + } + } + else if (cmi.type == pjmedia_type.PJMEDIA_TYPE_VIDEO && + cmi.status == pjsua_call_media_status.PJSUA_CALL_MEDIA_ACTIVE && + cmi.videoIncomingWindowId != pjsua2.INVALID_ID) + { + vidWin = new VideoWindow(cmi.videoIncomingWindowId); + vidPrev = new VideoPreview(cmi.videoCapDev); + } + } + + SoftApp.observer.notifyCallMediaState(this); + } + } +} \ No newline at end of file diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftLogWriter.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftLogWriter.cs new file mode 100644 index 0000000000..e71cab53d1 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Models/SoftLogWriter.cs @@ -0,0 +1,11 @@ +using libpjsua2.maui; + +namespace pjsua2maui.Models; + +public class SoftLogWriter : LogWriter +{ + override public void write(LogEntry entry) + { + Console.WriteLine(entry.msg); + } +} \ No newline at end of file diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/AndroidManifest.xml b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/AndroidManifest.xml new file mode 100644 index 0000000000..0271f91fc1 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/CallPageHandler.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/CallPageHandler.cs new file mode 100644 index 0000000000..28e46bc45f --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/CallPageHandler.cs @@ -0,0 +1,268 @@ +#if __ANDROID__ + +using Android.App; +using Android.Content; +using Android.Graphics; +using Android.Runtime; +using Android.Views; +using Android.Widget; +using CommunityToolkit.Mvvm.Messaging; +using libpjsua2.maui; +using Microsoft.Maui.Controls.Handlers.Compatibility; +using Microsoft.Maui.Controls.Platform; +using pjsua2maui.Controls; +using pjsua2maui.Messages; +using pjsua2maui.Models; +using pjsua2maui.Views; +using System.Runtime.InteropServices; +namespace pjsua2maui.Platforms.Android; + +public class CallPageRenderer : VisualElementRenderer, ISurfaceHolderCallback +{ + global::Android.Widget.Button acceptCallButton; + global::Android.Widget.Button hangupCallButton; + global::Android.Widget.TextView peerTxt; + global::Android.Widget.TextView statusTxt; + global::Android.Views.View view; + private static CallInfo lastCallInfo; + private CallPage callPage; + SurfaceView incomingView; + + [DllImport("android")] + private static extern IntPtr ANativeWindow_fromSurface(IntPtr jni, IntPtr surface); + + public CallPageRenderer(Context context) : base(context) + { + WeakReferenceMessenger.Default.Register(this, (r, m) => + { + lastCallInfo = m.Value as CallInfo; + Dispatcher.GetForCurrentThread().Dispatch(() => updateCallState(lastCallInfo)); + + if (lastCallInfo.state == + pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED) + { + Dispatcher.GetForCurrentThread().Dispatch(() => { callPage.Navigation.PopAsync(); }); + } + }); + + WeakReferenceMessenger.Default.Register(this, (r, m) => + { + lastCallInfo = m.Value as CallInfo; + + if (SoftApp.currentCall.vidWin != null) + { + incomingView.Visibility = ViewStates.Invisible; + } + }); + } + + ~CallPageRenderer() + { + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + } + + protected override void OnLayout(bool changed, int l, int t, int r, int b) + { + base.OnLayout(changed, l, t, r, b); + + var msw = MeasureSpec.MakeMeasureSpec(r - l, MeasureSpecMode.Exactly); + var msh = MeasureSpec.MakeMeasureSpec(b - t, MeasureSpecMode.Exactly); + + view.Measure(msw, msh); + view.Layout(0, 0, r - l, b - t); + } + + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + + if (e.OldElement != null || Element == null) + { + return; + } + + try + { + SetupUserInterface(); + SetupEventHandlers(); + AddView(view); + callPage = (CallPage)Element.Parent; + System.Diagnostics.Debug.WriteLine(@"Call page done initialize"); + if (SoftApp.currentCall != null) + { + try + { + lastCallInfo = SoftApp.currentCall.getInfo(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(@"ERROR: ", ex.Message); + } + Dispatcher.GetForCurrentThread().Dispatch(() => updateCallState(lastCallInfo)); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(@"ERROR: ", ex.Message); + } + } + + + void SetupUserInterface() + { + var activity = this.Context as Activity; + view = activity.LayoutInflater.Inflate(Resources.GetLayout(Resource.Layout.activity_call), this, false); + + incomingView = view.FindViewById(Resource.Id.incomingVideoView); + incomingView.Holder.AddCallback(this); + + peerTxt = view.FindViewById(Resource.Id.peerTxt); + statusTxt = view.FindViewById(Resource.Id.statusTxt); + + if (SoftApp.currentCall == null || SoftApp.currentCall.vidWin == null) + { + incomingView.Visibility = ViewStates.Gone; + } + } + + void SetupEventHandlers() + { + acceptCallButton = view.FindViewById(Resource.Id.acceptCallButton); + acceptCallButton.Click += AcceptCallButtonTapped; + + hangupCallButton = view.FindViewById(Resource.Id.hangupCallButton); + hangupCallButton.Click += HangupCallButtonTapped; + } + + void AcceptCallButtonTapped(object sender, EventArgs e) + { + CallOpParam prm = new CallOpParam(); + prm.statusCode = pjsip_status_code.PJSIP_SC_OK; + try + { + SoftApp.currentCall.answer(prm); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(@"ERROR: ", ex.Message); + } + + acceptCallButton.Visibility = ViewStates.Gone; + } + + void HangupCallButtonTapped(object sender, EventArgs e) + { + if (SoftApp.currentCall != null) + { + CallOpParam prm = new CallOpParam(); + prm.statusCode = pjsip_status_code.PJSIP_SC_DECLINE; + try + { + SoftApp.currentCall.hangup(prm); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(@"ERROR: ", ex.Message); + } + } + } + + private void updateVideoWindow(bool show) + { + if (SoftApp.currentCall != null && + SoftApp.currentCall.vidWin != null && + SoftApp.currentCall.vidPrev != null) + { + long windHandle = 0; + VideoWindowHandle vidWH = new VideoWindowHandle(); + if (show) + { + IntPtr winPtr = ANativeWindow_fromSurface(JNIEnv.Handle, + incomingView.Holder.Surface.Handle); + windHandle = winPtr.ToInt64(); + } + vidWH.handle.window = (nint)windHandle; + try + { + SoftApp.currentCall.vidWin.setWindow(vidWH); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(@"ERROR: ", ex.Message); + } + } + } + + private void updateCallState(CallInfo ci) + { + String call_state = ""; + + if (ci == null) + { + acceptCallButton.Visibility = ViewStates.Gone; + hangupCallButton.Text = "OK"; + statusTxt.Text = "Call disconnected"; + return; + } + + if (ci.role == pjsip_role_e.PJSIP_ROLE_UAC) + { + acceptCallButton.Visibility = ViewStates.Gone; + } + + if (ci.state < + pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED) + { + if (ci.role == pjsip_role_e.PJSIP_ROLE_UAS) + { + call_state = "Incoming call.."; + /* Default button texts are already 'Accept' & 'Reject' */ + } + else + { + hangupCallButton.Text = "Cancel"; + call_state = ci.stateText; + } + } + else if (ci.state >= + pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED) + { + acceptCallButton.Visibility = ViewStates.Gone; + call_state = ci.stateText; + if (ci.state == pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED) + { + hangupCallButton.Text = "Hangup"; + } + else if (ci.state == + pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED) + { + hangupCallButton.Text = "OK"; + call_state = "Call disconnected: " + ci.lastReason; + } + if (ci.state == pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED) + { + updateVideoWindow(true); + } + } + + peerTxt.Text = ci.remoteUri; + statusTxt.Text = call_state; + } + + void ISurfaceHolderCallback.SurfaceCreated(ISurfaceHolder holder) + { + updateVideoWindow(true); + } + + void ISurfaceHolderCallback.SurfaceDestroyed(ISurfaceHolder holder) + { + updateVideoWindow(false); + } + + public void SurfaceChanged(ISurfaceHolder holder, [GeneratedEnum] Format format, int width, int height) + { + updateVideoWindow(true); + } +} +#endif \ No newline at end of file diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/MainActivity.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/MainActivity.cs new file mode 100644 index 0000000000..e50222dc5c --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/MainActivity.cs @@ -0,0 +1,10 @@ +using Android.App; +using Android.Content.PM; +using Android.OS; + +namespace pjsua2maui; + +[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] +public class MainActivity : MauiAppCompatActivity +{ +} diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/MainApplication.cs b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/MainApplication.cs new file mode 100644 index 0000000000..b409c307e3 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/MainApplication.cs @@ -0,0 +1,35 @@ +using Android.App; +using Android.Hardware.Camera2; +using Android.Runtime; +using Java.Lang; + +namespace pjsua2maui; + +[Application] +public class MainApplication : MauiApplication +{ + public MainApplication(IntPtr handle, JniHandleOwnership ownership) + : base(handle, ownership) + { + IntPtr? class_ref = JNIEnv.FindClass("org/pjsip/PjCameraInfo2"); + if (class_ref != null) + { + IntPtr? method_id = JNIEnv.GetStaticMethodID(class_ref.Value, + "SetCameraManager", "(Landroid/hardware/camera2/CameraManager;)V"); + + if (method_id != null) + { + + CameraManager manager = GetSystemService(Android.Content.Context.CameraService) as CameraManager; + + JNIEnv.CallStaticVoidMethod(class_ref.Value, method_id.Value, new JValue(manager)); + + Console.WriteLine("SUCCESS setting cameraManager"); + } + } + JavaSystem.LoadLibrary("c++_shared"); + JavaSystem.LoadLibrary("pjsua2"); + } + + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); +} diff --git a/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/Resources/layout/activity_call.xml b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/Resources/layout/activity_call.xml new file mode 100644 index 0000000000..7f22449872 --- /dev/null +++ b/pjsip-apps/src/swig/csharp/pjsua2maui/pjsua2maui/Platforms/Android/Resources/layout/activity_call.xml @@ -0,0 +1,46 @@ + + + + + +