From 6fa44f9c43d6fa9754692325dcebc43344c7733a Mon Sep 17 00:00:00 2001 From: Oliver Epper Date: Wed, 12 Apr 2023 11:47:01 +0200 Subject: [PATCH 01/12] cleaner header --- src/phone/include/phone_instance_t.h | 2 +- src/phone/phone_instance_t.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/phone/include/phone_instance_t.h b/src/phone/include/phone_instance_t.h index 522d9bf..0bfd6c6 100644 --- a/src/phone/include/phone_instance_t.h +++ b/src/phone/include/phone_instance_t.h @@ -115,7 +115,7 @@ class phone_instance_t { PHONE_EXPORT bool is_thread_registered(); private: - void create_tls_transport_with_srv_lookup(); +// void create_tls_transport_with_srv_lookup(); std::unique_ptr m_ep; std::unique_ptr m_account; std::optional m_server; diff --git a/src/phone/phone_instance_t.cpp b/src/phone/phone_instance_t.cpp index 65ad416..cd281a9 100644 --- a/src/phone/phone_instance_t.cpp +++ b/src/phone/phone_instance_t.cpp @@ -57,11 +57,11 @@ void phone_instance_t::configure_opus(int channel_count, int complexity, int sam } } -void phone_instance_t::create_tls_transport_with_srv_lookup() { +void create_tls_transport_with_srv_lookup(pj::Endpoint &endpoint) { pj::TransportConfig t_cfg; t_cfg.port = 0; try { - m_ep->transportCreate(PJSIP_TRANSPORT_TLS, t_cfg); + endpoint.transportCreate(PJSIP_TRANSPORT_TLS, t_cfg); } catch (const pj::Error &e) { throw phone::exception{e.info()}; } @@ -80,7 +80,7 @@ void phone_instance_t::connect(std::string server, const std::string& user, std: acc_cfg.sipConfig.authCreds.push_back(cred_info); acc_cfg.regConfig.registrarUri = "sip:" + m_server.value() + ";transport=TLS"; - create_tls_transport_with_srv_lookup(); + create_tls_transport_with_srv_lookup(*m_ep); try { m_account->create(acc_cfg, true); From 598b8565cacfc1c08f1b3b190ef7f71a7ad95a99 Mon Sep 17 00:00:00 2001 From: Oliver Epper Date: Wed, 12 Apr 2023 11:58:01 +0200 Subject: [PATCH 02/12] delete old code --- src/phone/include/phone_instance_t.h | 1 - src/phone/phone_instance_t.cpp | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/phone/include/phone_instance_t.h b/src/phone/include/phone_instance_t.h index 0bfd6c6..d516999 100644 --- a/src/phone/include/phone_instance_t.h +++ b/src/phone/include/phone_instance_t.h @@ -115,7 +115,6 @@ class phone_instance_t { PHONE_EXPORT bool is_thread_registered(); private: -// void create_tls_transport_with_srv_lookup(); std::unique_ptr m_ep; std::unique_ptr m_account; std::optional m_server; diff --git a/src/phone/phone_instance_t.cpp b/src/phone/phone_instance_t.cpp index cd281a9..893d255 100644 --- a/src/phone/phone_instance_t.cpp +++ b/src/phone/phone_instance_t.cpp @@ -3,9 +3,10 @@ #include #include -phone_instance_t::phone_instance_t(std::string user_agent, std::vector nameserver, - std::vector stunserver) : m_ep{std::make_unique()}, - m_account{std::make_unique()} { +phone_instance_t::phone_instance_t(std::string user_agent, + std::vector nameserver, + std::vector stunserver) +: m_ep{std::make_unique()}, m_account{std::make_unique()} { pj::EpConfig ep_cfg{}; ep_cfg.uaConfig.userAgent = std::move(user_agent); ep_cfg.uaConfig.nameserver = std::move(nameserver); From 1c7d3a352f5727c0078335a5b749fda56bdc837d Mon Sep 17 00:00:00 2001 From: Oliver Epper Date: Wed, 12 Apr 2023 15:42:45 +0200 Subject: [PATCH 03/12] linter --- scripts/build-pjproject-catalyst-arm64.sh | 2 +- src/cpp_cli/simple_task_system.h | 2 +- src/phone/include/phone/version.h | 2 +- src/phone/version_c.cpp.in | 4 +--- src/python_cli/phone_ctypes.py | 10 +++++----- src/python_cli/python_cli.py | 1 + 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/scripts/build-pjproject-catalyst-arm64.sh b/scripts/build-pjproject-catalyst-arm64.sh index c065f26..d3df1bb 100644 --- a/scripts/build-pjproject-catalyst-arm64.sh +++ b/scripts/build-pjproject-catalyst-arm64.sh @@ -1,7 +1,7 @@ #!/bin/bash # Oliver Epper -source "$(dirname $0)"/build-pjproject-darwin-base.sh +source "$(dirname "$0")"/build-pjproject-darwin-base.sh PREFIX="${PREFIX}/catalyst-arm64" rm -rf "${PREFIX}" diff --git a/src/cpp_cli/simple_task_system.h b/src/cpp_cli/simple_task_system.h index 017fd9a..6bb80e1 100644 --- a/src/cpp_cli/simple_task_system.h +++ b/src/cpp_cli/simple_task_system.h @@ -16,7 +16,7 @@ class simple_task_system { phone_instance_t * m_phone_ptr; - void run(int i) { + void run(unsigned i) { m_phone_ptr->register_thread("worker_" + std::to_string(i)); while (true) { std::function f; diff --git a/src/phone/include/phone/version.h b/src/phone/include/phone/version.h index 6b42237..41a7c4e 100644 --- a/src/phone/include/phone/version.h +++ b/src/phone/include/phone/version.h @@ -2,7 +2,7 @@ #define PHONE_VERSION_H #include -#include +#include //NOLINT deprecated-headers #ifdef __cplusplus extern "C" diff --git a/src/phone/version_c.cpp.in b/src/phone/version_c.cpp.in index d9e8b9f..24ce1df 100644 --- a/src/phone/version_c.cpp.in +++ b/src/phone/version_c.cpp.in @@ -1,4 +1,3 @@ -//NOLINTBEGIN #include #include @@ -24,5 +23,4 @@ void phone_git_hash(char *out, size_t size) { void phone_git_description(char *out, size_t size) { strncpy(out, "@phone_GIT_DESCRIPTION@", size); -} -//NOLINTEND \ No newline at end of file +} \ No newline at end of file diff --git a/src/python_cli/phone_ctypes.py b/src/python_cli/phone_ctypes.py index 26f9088..1551561 100644 --- a/src/python_cli/phone_ctypes.py +++ b/src/python_cli/phone_ctypes.py @@ -267,7 +267,7 @@ def phone_get_audio_device_names(device_filter): # PHONE_EXPORT phone_status_t phone_get_audio_devices(audio_device_info_t *devices, size_t *devices_count, size_t max_driver_name_length, size_t max_device_name_length, device_filter_t filter); -class phone_audio_device_info: +class PhoneAudioDeviceInfo: def __init__(self, id, driver, name, input_count, output_count): self.id = id self.driver = driver @@ -277,7 +277,7 @@ def __init__(self, id, driver, name, input_count, output_count): def phone_get_audio_devices(device_filter): - class c_phone_audio_device_info_t(Structure): + class CPhoneAudioDeviceInfoT(Structure): _fields_ = [ ('id', c_int), ('driver', c_char_p), @@ -287,13 +287,13 @@ class c_phone_audio_device_info_t(Structure): ] __phone_get_audio_devices = libphone.phone_get_audio_devices __phone_get_audio_devices.restype = c_int - __phone_get_audio_devices.argtypes = [POINTER(c_phone_audio_device_info_t), POINTER(c_size_t), c_size_t, c_size_t, device_filter_t] + __phone_get_audio_devices.argtypes = [POINTER(CPhoneAudioDeviceInfoT), POINTER(c_size_t), c_size_t, c_size_t, device_filter_t] if not DEVICE_FILTER_NONE <= device_filter <= DEVICE_FILTER_OUTPUT: device_filter = DEVICE_FILTER_NONE c_count = c_size_t(phone_get_audio_devices_count()) max_device_driver_name_length = phone_get_audio_device_driver_name_length() + 1 # +1 for zero termination max_device_name_length = phone_get_audio_device_info_name_length() + 1 # +1 for zero termination - devices = (c_phone_audio_device_info_t * c_count.value)() + devices = (CPhoneAudioDeviceInfoT * c_count.value)() for i in range(c_count.value): devices[i].driver = cast(create_string_buffer(max_device_driver_name_length), c_char_p) @@ -303,7 +303,7 @@ class c_phone_audio_device_info_t(Structure): raise Exception(phone_last_error()) def to_python(c_phone_audio_device_info): - return phone_audio_device_info( + return PhoneAudioDeviceInfo( c_phone_audio_device_info.id, c_phone_audio_device_info.driver.decode('utf-8'), c_phone_audio_device_info.name.decode('utf-8'), diff --git a/src/python_cli/python_cli.py b/src/python_cli/python_cli.py index 0ba809b..eed6445 100644 --- a/src/python_cli/python_cli.py +++ b/src/python_cli/python_cli.py @@ -82,6 +82,7 @@ def on_call_state_index_cb(call_index, state, ctx): def on_call_state_id_cb(call_id, state, ctx): print(f"Call id: {call_id.decode('utf-8')} and index: {phone_get_call_index(phone, call_id)} – state: {phone_call_state_name(state)}") + phone_register_on_registration_state_callback(phone, on_registration_state_cb, None) phone_register_on_incoming_call_index_callback(phone, on_incoming_call_index_cb, None) phone_register_on_incoming_call_id_callback(phone, on_incoming_call_id_cb, None) From d3547d6b4a90fa2a0c47502192f332a9a3cd36da Mon Sep 17 00:00:00 2001 From: Oliver Epper Date: Thu, 13 Apr 2023 17:15:38 +0200 Subject: [PATCH 04/12] use system nameserver --- src/c_cli/main.c | 3 +- src/cpp_cli/main.cpp | 6 +++- src/phone/CMakeLists.txt | 4 ++- src/phone/include/phone.h | 3 ++ src/phone/include/phone_instance_t.h | 3 ++ src/phone/include/private/nameserver.h | 48 ++++++++++++++++++++++++++ src/phone/phone.cpp | 13 +++++-- src/phone/phone_instance_t.cpp | 4 +++ 8 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 src/phone/include/private/nameserver.h diff --git a/src/c_cli/main.c b/src/c_cli/main.c index 117d713..c13559f 100644 --- a/src/c_cli/main.c +++ b/src/c_cli/main.c @@ -85,9 +85,8 @@ int main() { memset(state->last_call_id, 0, sizeof(state->last_call_id)); // create phone in app state - const char *nameserver[] = {"217.237.148.22", "217.237.150.51"}; const char *stunserver[] = {"stun.t-online.de"}; - state->phone = phone_create("Cli Phone in C", nameserver, 2, stunserver, 1); + state->phone = phone_create_with_system_nameserver("Cli Phone in C", stunserver, 1); if (!state->phone) die(state->phone); diff --git a/src/cpp_cli/main.cpp b/src/cpp_cli/main.cpp index 705310f..734e304 100644 --- a/src/cpp_cli/main.cpp +++ b/src/cpp_cli/main.cpp @@ -89,7 +89,11 @@ struct app_state { auto main() -> int { try { app_state state{ - phone_instance_t{"Cli Phone in C++", {"217.237.148.22", "217.237.150.51"}, {"stun.t-online.de"}}}; + phone_instance_t{ + "Cli Phone in C++", + {"stun.t-online.de"} + }}; + phone_instance_t::set_log_level(0); // callbacks diff --git a/src/phone/CMakeLists.txt b/src/phone/CMakeLists.txt index cf8f79e..ce9ce46 100644 --- a/src/phone/CMakeLists.txt +++ b/src/phone/CMakeLists.txt @@ -37,13 +37,15 @@ generate_export_header(phone BASE_NAME "phone") target_compile_options(phone PUBLIC ${PJPROJECT_CFLAGS}) +find_library(RESOLV_LIBRARY resolv) + target_include_directories(phone PUBLIC include ${CMAKE_CURRENT_BINARY_DIR} PRIVATE include/phone) -target_link_libraries(phone PRIVATE ${PJPROJECT_LINK_LIBRARIES}) +target_link_libraries(phone PRIVATE ${PJPROJECT_LINK_LIBRARIES} ${RESOLV_LIBRARY}) set(phone_PUBLIC_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/phone.h diff --git a/src/phone/include/phone.h b/src/phone/include/phone.h index bb7c577..242efeb 100644 --- a/src/phone/include/phone.h +++ b/src/phone/include/phone.h @@ -33,6 +33,9 @@ typedef struct { PHONE_EXPORT phone_t phone_create(const char *user_agent, const char * const nameserver[], size_t nameserver_count, const char * const stunserver[], size_t stunserver_count); +PHONE_EXPORT phone_t phone_create_with_system_nameserver(const char *user_agent, + const char * const stunserver[], size_t stunserver_count); + PHONE_EXPORT void phone_destroy(phone_t instance); PHONE_EXPORT void phone_register_on_registration_state_callback(phone_t instance, void (*cb)(int is_registered, int registration_state, void *ctx), void *ctx); diff --git a/src/phone/include/phone_instance_t.h b/src/phone/include/phone_instance_t.h index d516999..a0d73fb 100644 --- a/src/phone/include/phone_instance_t.h +++ b/src/phone/include/phone_instance_t.h @@ -69,6 +69,9 @@ class phone_instance_t { std::vector nameserver, std::vector stunserver); + explicit PHONE_EXPORT phone_instance_t(std::string user_agent, + std::vector stunserver); + PHONE_EXPORT ~phone_instance_t(); phone_instance_t() = delete; diff --git a/src/phone/include/private/nameserver.h b/src/phone/include/private/nameserver.h new file mode 100644 index 0000000..1dc8613 --- /dev/null +++ b/src/phone/include/private/nameserver.h @@ -0,0 +1,48 @@ +// +// Created by Oliver Epper on 13.04.23. +// + +#ifndef PHONE_NAMESERVER_H +#define PHONE_NAMESERVER_H + +#include "../phone_instance_t.h" +#include +#include +#include +#include + +std::vector system_nameserver() { + struct __res_state res_state = {}; + + if (res_ninit(&res_state) != 0) + throw phone::exception("could not initialize resolver state"); + + int count = res_state.nscount; + struct sockaddr_storage addresses[count]; + res_getservers(&res_state, reinterpret_cast(addresses), count); + + std::vector server; + server.reserve(count); + + for (int i = 0; i < count; ++i) { + char ip_cstring[INET6_ADDRSTRLEN]; + auto address = reinterpret_cast(&addresses[i]); + + if (address->sa_family == AF_INET) { + auto ipv4_address = reinterpret_cast(address); + inet_ntop(AF_INET, &(ipv4_address->sin_addr), ip_cstring, sizeof(ip_cstring)); + server.emplace_back(ip_cstring); + } else if (address->sa_family == AF_INET6) { + auto ipv6_address = reinterpret_cast(address); + inet_ntop(AF_INET6, &(ipv6_address->sin6_addr), ip_cstring, sizeof(ip_cstring)); + server.emplace_back(ip_cstring); + } else { + continue; + } + } + res_nclose(&res_state); + + return server; +} + +#endif //PHONE_NAMESERVER_H diff --git a/src/phone/phone.cpp b/src/phone/phone.cpp index 01ac371..1cd5093 100644 --- a/src/phone/phone.cpp +++ b/src/phone/phone.cpp @@ -24,6 +24,17 @@ phone_t phone_create(const char *user_agent, } } +phone_t +phone_create_with_system_nameserver(const char *user_agent, const char *const *stunserver, size_t stunserver_count) { + const std::vector _stunserver(stunserver, stunserver + stunserver_count); + try { + return new phone_instance_t{user_agent,_stunserver}; + } catch (const phone::exception& e) { + strncpy(global_last_error, e.what(), sizeof(global_last_error)); + return nullptr; + } +} + void phone_register_on_registration_state_callback(phone_t instance, void (*cb)(int, int, void *), void *ctx) { instance->register_on_registration_state_callback([cb, ctx](bool is_registered, int registration_state) { cb(is_registered, registration_state, ctx); @@ -370,5 +381,3 @@ phone_status_t phone_register_thread(phone_t instance, const char *name) { int phone_is_thread_registered(phone_t instance) { return instance->is_thread_registered(); } - - diff --git a/src/phone/phone_instance_t.cpp b/src/phone/phone_instance_t.cpp index 893d255..94e8b40 100644 --- a/src/phone/phone_instance_t.cpp +++ b/src/phone/phone_instance_t.cpp @@ -1,5 +1,6 @@ #include "phone_instance_t.h" #include "private/account_t.h" +#include "private/nameserver.h" #include #include @@ -22,6 +23,9 @@ phone_instance_t::phone_instance_t(std::string user_agent, } } +phone_instance_t::phone_instance_t(std::string user_agent, std::vector stunserver) +: phone_instance_t(user_agent, system_nameserver(), stunserver) {} + phone_instance_t::~phone_instance_t() { m_ep->libDestroy(); } From 2a416027aac2b106b09e2bd089932dde7e77fcfb Mon Sep 17 00:00:00 2001 From: Oliver Epper Date: Thu, 13 Apr 2023 19:44:41 +0200 Subject: [PATCH 05/12] python love --- src/python_cli/phone_ctypes.py | 15 +++++++++++++++ src/python_cli/python_cli.py | 3 +-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/python_cli/phone_ctypes.py b/src/python_cli/phone_ctypes.py index 1551561..e8d0d3c 100644 --- a/src/python_cli/phone_ctypes.py +++ b/src/python_cli/phone_ctypes.py @@ -69,6 +69,21 @@ def phone_create(user_agent, nameservers, stunservers): return __phone_create(c_user_agent, c_nameservers, len(nameservers), c_stunservers, len(stunservers)) +# PHONE_EXPORT phone_t phone_create_with_system_nameserver(const char *user_agent, +# const char * const stunserver[], size_t stunserver_count); +def phone_create_with_system_nameserver(user_agent, stunservers): + __phone_create_with_system_nameserver = libphone.phone_create_with_system_nameserver + __phone_create_with_system_nameserver.restype = c_void_p + __phone_create_with_system_nameserver.argtypes = [c_char_p, POINTER(c_char_p), c_size_t] + c_user_agent = c_char_p(user_agent.encode('utf-8')) + + c_stunservers = (c_char_p * len(stunservers))() + for i in range(len(stunservers)): + c_stunservers[i] = c_char_p(stunservers[i].encode('utf-8')) + + return __phone_create_with_system_nameserver(c_user_agent, c_stunservers, len(stunservers)) + + # PHONE_EXPORT void phone_destroy(phone_t instance); phone_destroy = libphone.phone_destroy phone_destroy.restype = None diff --git a/src/python_cli/python_cli.py b/src/python_cli/python_cli.py index eed6445..99048ef 100644 --- a/src/python_cli/python_cli.py +++ b/src/python_cli/python_cli.py @@ -11,7 +11,6 @@ from phone_ctypes import * useragent = "Python CLI Phone" -nameservers = ["217.237.148.22", "217.237.150.51"] stunservers = ["stun.t-online.de"] sipserver = "tel.t-online.de" username = "+49..." @@ -26,7 +25,7 @@ exit(1) -phone = phone_create(useragent, nameservers, stunservers) +phone = phone_create_with_system_nameserver(useragent, stunservers) if phone is None: die(phone) phone_set_log_level(0) From ccc3ccf0caac31a0025d9256c95612906064d7ed Mon Sep 17 00:00:00 2001 From: Oliver Epper Date: Thu, 13 Apr 2023 19:53:25 +0200 Subject: [PATCH 06/12] java love --- .../src/main/java/de/oliver_epper/libphone/Main.java | 5 +---- .../src/main/java/de/oliver_epper/libphone/Phone.java | 10 ++++++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Main.java b/src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Main.java index 04d0f34..419cd52 100644 --- a/src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Main.java +++ b/src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Main.java @@ -7,16 +7,13 @@ public class Main { public static void main(String[] args) { var executor = Executors.newFixedThreadPool(1); - var nameservers = new String[2]; - nameservers[0] = "217.237.148.22"; - nameservers[1] = "217.237.150.51"; var stunservers = new String[1]; stunservers[0] = "stun.t-online.de"; final Phone phone; try { - phone = new Phone("Java Cli ☕️", nameservers, stunservers); + phone = new Phone("Java Cli ☕️", stunservers); Runnable register = () -> phone.registerThread("Test"); diff --git a/src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Phone.java b/src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Phone.java index e74e12b..2c92f74 100644 --- a/src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Phone.java +++ b/src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Phone.java @@ -59,6 +59,9 @@ interface CallStateIdCallback extends Callback { // PHONE_EXPORT phone_t phone_create(const char *user_agent, const char * const nameserver[], size_t nameserver_count, const char * const stunserver[], size_t stunserver_count); Pointer phone_create(String userAgent, String[] nameServers, NativeLong nameServerLength, String[] stunServers, NativeLong stunServerLength); + // PHONE_EXPORT phone_t phone_create_with_system_nameserver(const char *user_agent, const char * const stunserver[], size_t stunserver_count); + Pointer phone_create_with_system_nameserver(String userAgent, String[] stunServers, NativeLong stunServerLength); + // PHONE_EXPORT void phone_destroy(phone_t instance); void phone_destroy(Pointer instance); @@ -204,6 +207,13 @@ public Phone(String userAgent, String[] nameservers, String[] stunservers) throw } } + public Phone(String userAgent, String[] stunservers) throws PhoneException { + this.phone = CPHONE.phone_create_with_system_nameserver(userAgent, stunservers, new NativeLong(stunservers.length)); + if(this.phone == null) { + throw new PhoneException(lastError()); + } + } + void destroy() { CPHONE.phone_destroy(phone); } From a4ecbb4a350c11c58306d1877b17ce614934f52c Mon Sep 17 00:00:00 2001 From: Oliver Epper Date: Fri, 14 Apr 2023 09:42:38 +0200 Subject: [PATCH 07/12] linter --- src/phone/phone_instance_t.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phone/phone_instance_t.cpp b/src/phone/phone_instance_t.cpp index 94e8b40..4a69aec 100644 --- a/src/phone/phone_instance_t.cpp +++ b/src/phone/phone_instance_t.cpp @@ -24,7 +24,7 @@ phone_instance_t::phone_instance_t(std::string user_agent, } phone_instance_t::phone_instance_t(std::string user_agent, std::vector stunserver) -: phone_instance_t(user_agent, system_nameserver(), stunserver) {} +: phone_instance_t(std::move(user_agent), system_nameserver(), std::move(stunserver)) {} phone_instance_t::~phone_instance_t() { m_ep->libDestroy(); From e9f1e99a988eaf49f1e97a68061095059cda8bfc Mon Sep 17 00:00:00 2001 From: Oliver Epper Date: Fri, 14 Apr 2023 10:38:42 +0200 Subject: [PATCH 08/12] dtmf via RFC 2833 --- src/cpp_cli/main.cpp | 29 ++++++++++++++++++++++----- src/phone/include/phone_instance_t.h | 2 ++ src/phone/include/private/account_t.h | 5 +++++ src/phone/phone_instance_t.cpp | 19 ++++++++++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/cpp_cli/main.cpp b/src/cpp_cli/main.cpp index 734e304..a6d35e7 100644 --- a/src/cpp_cli/main.cpp +++ b/src/cpp_cli/main.cpp @@ -10,7 +10,7 @@ struct app_state { phone_instance_t phone; std::string last_call_id; - int last_call_index; + int last_call_index = -1; simple_task_system task_system{&phone}; [[maybe_unused]] static void on_registration_state(bool is_registered, int registration_state) { @@ -77,12 +77,20 @@ struct app_state { } } - [[maybe_unused]] static void on_call_state_with_index_cb(int call_index, int state) { + void on_call_state_with_index_cb(int call_index, int state) { + last_call_index = call_index; std::cout << "Update for call index: " << call_index << " – state: " << phone::call_state_name(state) << std::endl; + if (phone::call_state_name(state) == "DISCONNECTED") { + last_call_index = -1; + } } - [[maybe_unused]] static void on_call_state_with_id_cb(const std::string& call_id, int state) { + void on_call_state_with_id_cb(const std::string& call_id, int state) { + last_call_id = call_id; std::cout << "Update for call id: " << call_id << " – state: " << phone::call_state_name(state) << std::endl; + if (phone::call_state_name(state) == "DISCONNECTED") { + last_call_id = ""; + } } }; @@ -106,8 +114,12 @@ auto main() -> int { state.phone.register_on_incoming_call_callback([&state](const std::string& call_id) { state.on_incoming_call_cb(call_id); }); - state.phone.register_on_call_state_callback(app_state::on_call_state_with_index_cb); - state.phone.register_on_call_state_callback(app_state::on_call_state_with_id_cb); + state.phone.register_on_call_state_callback([&state](int call_index, int call_state) { + state.on_call_state_with_index_cb(call_index, call_state); + }); + state.phone.register_on_call_state_callback([&state](const std::string& call_id, int call_state) { + state.on_call_state_with_id_cb(call_id, call_state); + }); // opus state.phone.configure_opus(); @@ -175,6 +187,13 @@ auto main() -> int { std::cout << "please enter desired playback device: "; std::cin >> playback_index; phone_instance_t::set_audio_devices(capture_index, playback_index); + } else if (command == 'p') { + std::cout << "please enter dtmf characters: "; + std::string dtmf_digits; + std::cin >> dtmf_digits; + if (state.last_call_index != -1) { + state.phone.dtmf(state.last_call_index, dtmf_digits); + } } } catch (const phone::exception& e) { std::cerr << "Error: " << e.what() << std::endl; diff --git a/src/phone/include/phone_instance_t.h b/src/phone/include/phone_instance_t.h index a0d73fb..9f50630 100644 --- a/src/phone/include/phone_instance_t.h +++ b/src/phone/include/phone_instance_t.h @@ -98,6 +98,8 @@ class phone_instance_t { PHONE_EXPORT void start_ringing_call(std::string call_id); PHONE_EXPORT void hangup_call(int call_index); PHONE_EXPORT void hangup_call(std::string call_id); + PHONE_EXPORT void dtmf(int call_index, const std::string& digits); + PHONE_EXPORT void dtmf(std::string call_id, const std::string& digits); PHONE_EXPORT void hangup_calls() noexcept; PHONE_EXPORT std::string get_call_id(int call_index); diff --git a/src/phone/include/private/account_t.h b/src/phone/include/private/account_t.h index 6f145c5..931355a 100644 --- a/src/phone/include/private/account_t.h +++ b/src/phone/include/private/account_t.h @@ -126,6 +126,11 @@ class account_t : public pj::Account { call->hangup(prm); } + void dial_dtmf_in_call(phone::CallID auto id, const std::string& digits) { + call_t *call = find_call(id); + call->dialDtmf(digits); + } + std::optional call_incoming_message(phone::CallID auto id) { call_t *call = find_call(id); return call->incoming_message; diff --git a/src/phone/phone_instance_t.cpp b/src/phone/phone_instance_t.cpp index 4a69aec..71c3356 100644 --- a/src/phone/phone_instance_t.cpp +++ b/src/phone/phone_instance_t.cpp @@ -156,6 +156,24 @@ void phone_instance_t::hangup_call(std::string call_id) { } } +void phone_instance_t::dtmf(int call_index, const std::string &digits) { + try { + m_account->dial_dtmf_in_call(call_index, digits); + } catch (const pj::Error& e) { + throw phone::exception{e.info()}; + } + +} + +void phone_instance_t::dtmf(std::string call_id, const std::string &digits) { + try { + m_account->dial_dtmf_in_call(std::move(call_id), digits); + } catch (const pj::Error& e) { + throw phone::exception{e.info()}; + } + +} + void phone_instance_t::hangup_calls() noexcept { m_account->hangup_calls(); } @@ -266,3 +284,4 @@ void phone_instance_t::register_thread(const std::string &name) { bool phone_instance_t::is_thread_registered() { return m_ep->libIsThreadRegistered(); } + From f0ec7ef7b346b3e03638860e5f88367c28627573 Mon Sep 17 00:00:00 2001 From: Oliver Epper Date: Mon, 17 Apr 2023 10:45:38 +0200 Subject: [PATCH 09/12] dtmf and call_waiting --- src/cpp_cli/main.cpp | 6 +++++ src/phone/include/phone_instance_t.h | 6 +++++ src/phone/include/private/account_t.h | 2 +- src/phone/include/private/tones.h | 37 +++++++++++++++++++++++++++ src/phone/phone_instance_t.cpp | 26 ++++++++++++++++--- 5 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 src/phone/include/private/tones.h diff --git a/src/cpp_cli/main.cpp b/src/cpp_cli/main.cpp index a6d35e7..bf974ea 100644 --- a/src/cpp_cli/main.cpp +++ b/src/cpp_cli/main.cpp @@ -194,6 +194,12 @@ auto main() -> int { if (state.last_call_index != -1) { state.phone.dtmf(state.last_call_index, dtmf_digits); } + } else if (command == 'b') { + std::cout << "playing call waiting" << std::endl; + state.phone.play_call_waiting(); + } else if (command == 'B') { + std::cout << "stop call waiting" << std::endl; + state.phone.stop_call_waiting(); } } catch (const phone::exception& e) { std::cerr << "Error: " << e.what() << std::endl; diff --git a/src/phone/include/phone_instance_t.h b/src/phone/include/phone_instance_t.h index 9f50630..539cae5 100644 --- a/src/phone/include/phone_instance_t.h +++ b/src/phone/include/phone_instance_t.h @@ -10,6 +10,7 @@ namespace pj { class Endpoint; +class ToneGenerator; } //namespace pj class account_t; @@ -119,10 +120,15 @@ class phone_instance_t { PHONE_EXPORT void register_thread(const std::string& name); PHONE_EXPORT bool is_thread_registered(); + PHONE_EXPORT void play_call_waiting(); + PHONE_EXPORT void stop_call_waiting(); + private: std::unique_ptr m_ep; std::unique_ptr m_account; std::optional m_server; + std::unique_ptr m_call_waiting_tone_generator; + std::unique_ptr m_dtmf_tone_generator; }; #endif //PHONE_INSTANCE_T_H \ No newline at end of file diff --git a/src/phone/include/private/account_t.h b/src/phone/include/private/account_t.h index 931355a..b33ea88 100644 --- a/src/phone/include/private/account_t.h +++ b/src/phone/include/private/account_t.h @@ -126,7 +126,7 @@ class account_t : public pj::Account { call->hangup(prm); } - void dial_dtmf_in_call(phone::CallID auto id, const std::string& digits) { + void dial_dtmf(phone::CallID auto id, const std::string& digits) { call_t *call = find_call(id); call->dialDtmf(digits); } diff --git a/src/phone/include/private/tones.h b/src/phone/include/private/tones.h new file mode 100644 index 0000000..0b561d5 --- /dev/null +++ b/src/phone/include/private/tones.h @@ -0,0 +1,37 @@ +// +// Created by Oliver Epper on 17.04.23. +// + +#ifndef PHONE_TONES_H +#define PHONE_TONES_H + +#include + +pj::ToneDescVector call_waiting_sequence() { + pj::ToneDescVector sequence{}; + pj::ToneDesc tone; + tone.freq1 = 233; + tone.freq2 = 466; + tone.on_msec = 500; + tone.off_msec = 100; + tone.volume = 3500; + sequence.push_back(tone); + tone.off_msec = 4000; + sequence.push_back(tone); + return sequence; +} + +void play_tones(pj::ToneGenerator *generator, pj::AudioMedia &sink, const std::string &digits) { + pj::ToneDigitVector tone_digits; + for (const char c : digits) { + pj::ToneDigit tone_digit; + tone_digit.digit = c; + tone_digit.on_msec = 100; + tone_digit.off_msec = 20; + tone_digit.volume = 5000; + tone_digits.push_back(tone_digit); + } + generator->playDigits(tone_digits); +} + +#endif //PHONE_TONES_H diff --git a/src/phone/phone_instance_t.cpp b/src/phone/phone_instance_t.cpp index 71c3356..646dc81 100644 --- a/src/phone/phone_instance_t.cpp +++ b/src/phone/phone_instance_t.cpp @@ -1,6 +1,7 @@ #include "phone_instance_t.h" #include "private/account_t.h" #include "private/nameserver.h" +#include "private/tones.h" #include #include @@ -21,12 +22,20 @@ phone_instance_t::phone_instance_t(std::string user_agent, } catch (const pj::Error& e) { throw phone::exception{e.info()}; } + + m_call_waiting_tone_generator = std::make_unique(); + m_call_waiting_tone_generator->createToneGenerator(); + + m_dtmf_tone_generator = std::make_unique(); + m_dtmf_tone_generator->createToneGenerator(); + m_dtmf_tone_generator->startTransmit(m_ep->audDevManager().getPlaybackDevMedia()); } phone_instance_t::phone_instance_t(std::string user_agent, std::vector stunserver) : phone_instance_t(std::move(user_agent), system_nameserver(), std::move(stunserver)) {} phone_instance_t::~phone_instance_t() { + m_dtmf_tone_generator->stopTransmit(m_ep->audDevManager().getPlaybackDevMedia()); m_ep->libDestroy(); } @@ -158,20 +167,20 @@ void phone_instance_t::hangup_call(std::string call_id) { void phone_instance_t::dtmf(int call_index, const std::string &digits) { try { - m_account->dial_dtmf_in_call(call_index, digits); + m_account->dial_dtmf(call_index, digits); + play_tones(m_dtmf_tone_generator.get(), m_ep->audDevManager().getPlaybackDevMedia(), digits); } catch (const pj::Error& e) { throw phone::exception{e.info()}; } - } void phone_instance_t::dtmf(std::string call_id, const std::string &digits) { try { - m_account->dial_dtmf_in_call(std::move(call_id), digits); + m_account->dial_dtmf(std::move(call_id), digits); + play_tones(m_dtmf_tone_generator.get(), m_ep->audDevManager().getPlaybackDevMedia(), digits); } catch (const pj::Error& e) { throw phone::exception{e.info()}; } - } void phone_instance_t::hangup_calls() noexcept { @@ -285,3 +294,12 @@ bool phone_instance_t::is_thread_registered() { return m_ep->libIsThreadRegistered(); } +void phone_instance_t::play_call_waiting() { + m_call_waiting_tone_generator->startTransmit(m_ep->audDevManager().getPlaybackDevMedia()); + m_call_waiting_tone_generator->play(call_waiting_sequence(), true); +} + +void phone_instance_t::stop_call_waiting() { + m_call_waiting_tone_generator->stopTransmit(m_ep->audDevManager().getPlaybackDevMedia()); +} + From 69ed46c2226508a593581b6660c7db625380a259 Mon Sep 17 00:00:00 2001 From: Oliver Epper Date: Mon, 17 Apr 2023 11:58:35 +0200 Subject: [PATCH 10/12] bump version - dtmf - improvements to C++ Api safety - use system nameservers ipv4 and ipv6 - ability to register a custom log function - new Api call to handle ip change --- CMakeLists.txt | 2 +- src/c_cli/main.c | 28 ++++++ src/cpp_cli/main.cpp | 11 +++ src/phone/include/phone.h | 11 +++ src/phone/include/phone_instance_t.h | 33 ++++--- src/phone/include/private/account_t.h | 6 ++ src/phone/include/private/filename.h | 15 ++++ src/phone/include/private/log_writer_t.h | 21 +++++ .../{nameserver.h => system_nameserver.h} | 32 +++++-- .../{tones.h => tone_generator_helper.h} | 16 ++-- src/phone/phone.cpp | 63 +++++++++++++ src/phone/phone_instance_t.cpp | 90 +++++++++++++------ 12 files changed, 274 insertions(+), 54 deletions(-) create mode 100644 src/phone/include/private/filename.h create mode 100644 src/phone/include/private/log_writer_t.h rename src/phone/include/private/{nameserver.h => system_nameserver.h} (58%) rename src/phone/include/private/{tones.h => tone_generator_helper.h} (67%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 938ce28..5a1d038 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.3) -project(phone VERSION 0.6.1 LANGUAGES C CXX) +project(phone VERSION 0.7.0 LANGUAGES C CXX) set(CMAKE_CXX_STANDARD 20) diff --git a/src/c_cli/main.c b/src/c_cli/main.c index c13559f..446331e 100644 --- a/src/c_cli/main.c +++ b/src/c_cli/main.c @@ -79,6 +79,10 @@ void on_call_state_with_id_cb(const char* call_id, int state, void *ctx) { printf("Call %s – state: %s\n", call_id, buffer); } +void log_function(int level, const char *message, long thread_id, const char *thread_name) { + fprintf(stdout, "%s", message); +} + int main() { struct app_state *state = malloc(sizeof(struct app_state)); state->last_call_index = -1; @@ -92,6 +96,7 @@ int main() { // logging phone_set_log_level(0); + phone_set_log_function(state->phone, log_function); // callbacks phone_register_on_registration_state_callback(state->phone, on_registration_state, NULL); @@ -234,6 +239,29 @@ int main() { fprintf(stderr, "%s\n", phone_last_error()); } break; + case 'p': + clear_input_buffer(); + { + char dtmf_chars[128]; + printf("please dtmf characters: "); + if (read_string(dtmf_chars, sizeof(dtmf_chars)) != 0) break; + if (phone_play_dtmf_call_id(state->phone, state->last_call_id, dtmf_chars) != PHONE_STATUS_SUCCESS) + fprintf(stderr, "%s\n", phone_last_error()); + } + break; + case 'b': + phone_play_call_waiting(state->phone); + break; + case 'B': + phone_stop_call_waiting(state->phone); + break; + case '#': + printf("call count: %d\n", phone_get_call_count(state->phone)); + break; + case 'i': + printf("handle ip change\n"); + phone_handle_ip_change(); + break; default: clear_input_buffer(); break; diff --git a/src/cpp_cli/main.cpp b/src/cpp_cli/main.cpp index bf974ea..b210b32 100644 --- a/src/cpp_cli/main.cpp +++ b/src/cpp_cli/main.cpp @@ -102,8 +102,14 @@ auto main() -> int { {"stun.t-online.de"} }}; + // set log level phone_instance_t::set_log_level(0); + // and log function + state.phone.set_log_function([](int level, std::string_view message, long thread_id, std::string_view thread_name){ + std::cout << message; + }); + // callbacks state.phone.register_on_registration_state_callback([](bool is_registered, int registration_state) { app_state::on_registration_state(is_registered, registration_state); @@ -200,6 +206,11 @@ auto main() -> int { } else if (command == 'B') { std::cout << "stop call waiting" << std::endl; state.phone.stop_call_waiting(); + } else if (command == '#') { + std::cout << "call count: " << state.phone.get_call_count() << std::endl; + } else if (command == 'i') { + std::cout << "handle ip change" << std::endl; + phone_instance_t::handle_ip_change(); } } catch (const phone::exception& e) { std::cerr << "Error: " << e.what() << std::endl; diff --git a/src/phone/include/phone.h b/src/phone/include/phone.h index 242efeb..168785e 100644 --- a/src/phone/include/phone.h +++ b/src/phone/include/phone.h @@ -64,6 +64,9 @@ PHONE_DEPRECATED_EXPORT phone_status_t phone_hangup_call(phone_t instance, int c PHONE_EXPORT phone_status_t phone_hangup_call_index(phone_t instance, int call_index); PHONE_EXPORT phone_status_t phone_hangup_call_id(phone_t instance, const char *call_id); +PHONE_EXPORT phone_status_t phone_play_dtmf_call_index(phone_t instance, int call_index, const char *digits); +PHONE_EXPORT phone_status_t phone_play_dtmf_call_id(phone_t instance, const char *call_id, const char *digits); + PHONE_EXPORT void phone_hangup_calls(phone_t instance); PHONE_EXPORT phone_status_t phone_get_call_id(phone_t instance, int call_index, char *call_id, size_t size); @@ -142,10 +145,18 @@ PHONE_EXPORT void phone_call_state_name(char *out, size_t buffer_size, int state */ PHONE_DEPRECATED_EXPORT void phone_state_name(char *out, size_t buffer_size, int state); PHONE_EXPORT void phone_set_log_level(int level); +PHONE_EXPORT void phone_set_log_function(phone_t instance, void (*fn)(int level, const char *message, long thread_id, const char *thread_name)); PHONE_EXPORT phone_status_t phone_register_thread(phone_t instance, const char *name); PHONE_EXPORT int phone_is_thread_registered(phone_t instance); +PHONE_EXPORT phone_status_t phone_play_call_waiting(phone_t instance); +PHONE_EXPORT phone_status_t phone_stop_call_waiting(phone_t instance); + +PHONE_EXPORT unsigned phone_get_call_count(phone_t instance); + +PHONE_EXPORT phone_status_t phone_handle_ip_change(void); + #ifdef __cplusplus } #endif diff --git a/src/phone/include/phone_instance_t.h b/src/phone/include/phone_instance_t.h index 539cae5..a6dbe32 100644 --- a/src/phone/include/phone_instance_t.h +++ b/src/phone/include/phone_instance_t.h @@ -14,6 +14,7 @@ class ToneGenerator; } //namespace pj class account_t; +class log_writer_t; namespace phone { struct PHONE_EXPORT exception : public std::exception { @@ -99,29 +100,34 @@ class phone_instance_t { PHONE_EXPORT void start_ringing_call(std::string call_id); PHONE_EXPORT void hangup_call(int call_index); PHONE_EXPORT void hangup_call(std::string call_id); - PHONE_EXPORT void dtmf(int call_index, const std::string& digits); - PHONE_EXPORT void dtmf(std::string call_id, const std::string& digits); + PHONE_EXPORT void dtmf(int call_index, const std::string& digits) const; + PHONE_EXPORT void dtmf(std::string call_id, const std::string& digits) const; PHONE_EXPORT void hangup_calls() noexcept; - PHONE_EXPORT std::string get_call_id(int call_index); - PHONE_EXPORT int get_call_index(const std::string& call_id); + [[nodiscard]] PHONE_EXPORT std::string get_call_id(int call_index) const; + [[nodiscard]] PHONE_EXPORT int get_call_index(const std::string& call_id) const; PHONE_EXPORT static void set_log_level(int level); + PHONE_EXPORT void set_log_function(const std::function& log_function); PHONE_EXPORT static void refresh_audio_devices(); - PHONE_EXPORT static std::vector get_audio_devices(); + [[nodiscard]] PHONE_EXPORT static std::vector get_audio_devices(); PHONE_EXPORT static void set_audio_devices(int capture_index, int playback_index); - PHONE_EXPORT std::optional call_incoming_message(int call_index); - PHONE_EXPORT std::optional call_incoming_message(const std::string& call_id); - PHONE_EXPORT std::optional call_answer_after(int call_index); - PHONE_EXPORT std::optional call_answer_after(const std::string& call_id); + [[nodiscard]] PHONE_EXPORT std::optional call_incoming_message(int call_index) const; + [[nodiscard]] PHONE_EXPORT std::optional call_incoming_message(const std::string& call_id) const; + [[nodiscard]] PHONE_EXPORT std::optional call_answer_after(int call_index) const; + [[nodiscard]] PHONE_EXPORT std::optional call_answer_after(const std::string& call_id) const; PHONE_EXPORT void register_thread(const std::string& name); - PHONE_EXPORT bool is_thread_registered(); + [[nodiscard]] PHONE_EXPORT bool is_thread_registered() const; - PHONE_EXPORT void play_call_waiting(); - PHONE_EXPORT void stop_call_waiting(); + PHONE_EXPORT void play_call_waiting() const; + PHONE_EXPORT void stop_call_waiting() const; + + [[nodiscard]] PHONE_EXPORT int get_call_count(); + + PHONE_EXPORT static void handle_ip_change(); private: std::unique_ptr m_ep; @@ -129,6 +135,9 @@ class phone_instance_t { std::optional m_server; std::unique_ptr m_call_waiting_tone_generator; std::unique_ptr m_dtmf_tone_generator; + // FIXME: hopefully pjsip fixes the assumption about beeing the owner of the *log_writer_t + // https://github.com/pjsip/pjproject/issues/3511 + log_writer_t *m_log_writer; }; #endif //PHONE_INSTANCE_T_H \ No newline at end of file diff --git a/src/phone/include/private/account_t.h b/src/phone/include/private/account_t.h index b33ea88..3640965 100644 --- a/src/phone/include/private/account_t.h +++ b/src/phone/include/private/account_t.h @@ -149,6 +149,12 @@ class account_t : public pj::Account { return static_cast(*find_call(call_id)); } + int get_call_count() { + unsigned call_count = pjsua_call_get_count(); + assert(call_count == m_calls.size()); + return call_count; + } + void delete_call(int call_index) noexcept { PJ_LOG(3, (__BASE_FILE__, "Going to delete call: %d", call_index)); auto it = call_iterator(call_index); diff --git a/src/phone/include/private/filename.h b/src/phone/include/private/filename.h new file mode 100644 index 0000000..eb3fa75 --- /dev/null +++ b/src/phone/include/private/filename.h @@ -0,0 +1,15 @@ +// +// Created by Oliver Epper on 18.04.23. +// + +#ifndef PHONE_FILENAME_H +#define PHONE_FILENAME_H + +#include + +std::string filename(const std::string& filepath) { + std::filesystem::path path(filepath); + return path.filename().string(); +} + +#endif //PHONE_FILENAME_H diff --git a/src/phone/include/private/log_writer_t.h b/src/phone/include/private/log_writer_t.h new file mode 100644 index 0000000..0b47dba --- /dev/null +++ b/src/phone/include/private/log_writer_t.h @@ -0,0 +1,21 @@ +// +// Created by Oliver Epper on 18.04.23. +// + +#ifndef PHONE_LOG_WRITER_T_H +#define PHONE_LOG_WRITER_T_H + +#include + +struct log_writer_t : public pj::LogWriter { + std::function log_function = + [](int level, std::string_view msg, long thread_id, std::string_view thread_name){ + pj_log_write(level, msg.data(), msg.length()); + }; + + void write(const pj::LogEntry &entry) override { + log_function(entry.level, entry.msg, entry.threadId, entry.threadName); + } +}; + +#endif //PHONE_LOG_WRITER_T_H diff --git a/src/phone/include/private/nameserver.h b/src/phone/include/private/system_nameserver.h similarity index 58% rename from src/phone/include/private/nameserver.h rename to src/phone/include/private/system_nameserver.h index 1dc8613..8c1ba42 100644 --- a/src/phone/include/private/nameserver.h +++ b/src/phone/include/private/system_nameserver.h @@ -2,10 +2,9 @@ // Created by Oliver Epper on 13.04.23. // -#ifndef PHONE_NAMESERVER_H -#define PHONE_NAMESERVER_H +#ifndef PHONE_SYSTEM_NAMESERVER_H +#define PHONE_SYSTEM_NAMESERVER_H -#include "../phone_instance_t.h" #include #include #include @@ -18,15 +17,33 @@ std::vector system_nameserver() { throw phone::exception("could not initialize resolver state"); int count = res_state.nscount; - struct sockaddr_storage addresses[count]; - res_getservers(&res_state, reinterpret_cast(addresses), count); std::vector server; server.reserve(count); + +#ifdef __linux__ + for (int i = 0; i < count; ++i) { + char ip_cstring[INET6_ADDRSTRLEN]; + + auto ipv4 = res_state.nsaddr_list[i]; + if (ipv4.sin_family == AF_INET) { + inet_ntop(AF_INET, &ipv4.sin_addr, ip_cstring, sizeof(ip_cstring)); + server.emplace_back(ip_cstring); + } + + auto ipv6 = res_state._u._ext.nsaddrs[i]; + if (ipv6 && ipv6->sin6_family == AF_INET6) { + inet_ntop(AF_INET6, &ipv6->sin6_addr, ip_cstring, sizeof(ip_cstring)); + server.emplace_back(ip_cstring); + } + } +#else + struct sockaddr_storage addresses[count]; + res_getservers(&res_state, reinterpret_cast(addresses), count); for (int i = 0; i < count; ++i) { char ip_cstring[INET6_ADDRSTRLEN]; - auto address = reinterpret_cast(&addresses[i]); + auto address = reinterpret_cast(&addresses[i]); if (address->sa_family == AF_INET) { auto ipv4_address = reinterpret_cast(address); @@ -40,9 +57,10 @@ std::vector system_nameserver() { continue; } } +#endif res_nclose(&res_state); return server; } -#endif //PHONE_NAMESERVER_H +#endif //PHONE_SYSTEM_NAMESERVER_H diff --git a/src/phone/include/private/tones.h b/src/phone/include/private/tone_generator_helper.h similarity index 67% rename from src/phone/include/private/tones.h rename to src/phone/include/private/tone_generator_helper.h index 0b561d5..61ec583 100644 --- a/src/phone/include/private/tones.h +++ b/src/phone/include/private/tone_generator_helper.h @@ -2,26 +2,26 @@ // Created by Oliver Epper on 17.04.23. // -#ifndef PHONE_TONES_H -#define PHONE_TONES_H +#ifndef PHONE_TONE_GENERATOR_HELPER_H +#define PHONE_TONE_GENERATOR_HELPER_H #include pj::ToneDescVector call_waiting_sequence() { pj::ToneDescVector sequence{}; pj::ToneDesc tone; - tone.freq1 = 233; - tone.freq2 = 466; + tone.freq1 = 466; + tone.freq2 = 932; tone.on_msec = 500; tone.off_msec = 100; - tone.volume = 3500; + tone.volume = 5000; sequence.push_back(tone); tone.off_msec = 4000; sequence.push_back(tone); return sequence; } -void play_tones(pj::ToneGenerator *generator, pj::AudioMedia &sink, const std::string &digits) { +void play_tones(pj::ToneGenerator &generator, const std::string &digits) { pj::ToneDigitVector tone_digits; for (const char c : digits) { pj::ToneDigit tone_digit; @@ -31,7 +31,7 @@ void play_tones(pj::ToneGenerator *generator, pj::AudioMedia &sink, const std::s tone_digit.volume = 5000; tone_digits.push_back(tone_digit); } - generator->playDigits(tone_digits); + generator.playDigits(tone_digits); } -#endif //PHONE_TONES_H +#endif //PHONE_TONE_GENERATOR_HELPER_H diff --git a/src/phone/phone.cpp b/src/phone/phone.cpp index 1cd5093..a385082 100644 --- a/src/phone/phone.cpp +++ b/src/phone/phone.cpp @@ -187,6 +187,26 @@ void phone_hangup_calls(phone_t instance) { instance->hangup_calls(); } +phone_status_t phone_play_dtmf_call_index(phone_t instance, int call_index, const char *digits) { + try { + instance->dtmf(call_index, digits); + } catch (const phone::exception& e) { + strncpy(global_last_error, e.what(), sizeof(global_last_error)); + return PHONE_STATUS_FAILURE; + } + return PHONE_STATUS_SUCCESS; +} + +phone_status_t phone_play_dtmf_call_id(phone_t instance, const char *call_id, const char *digits) { + try { + instance->dtmf(call_id, digits); + } catch (const phone::exception& e) { + strncpy(global_last_error, e.what(), sizeof(global_last_error)); + return PHONE_STATUS_FAILURE; + } + return PHONE_STATUS_SUCCESS; +} + phone_status_t phone_get_call_id(phone_t instance, int call_index, char *call_id, size_t size) { try { auto id = instance->get_call_id(call_index); @@ -381,3 +401,46 @@ phone_status_t phone_register_thread(phone_t instance, const char *name) { int phone_is_thread_registered(phone_t instance) { return instance->is_thread_registered(); } + +phone_status_t phone_play_call_waiting(phone_t instance) { + try { + instance->play_call_waiting(); + } catch (const phone::exception& e) { + strncpy(global_last_error, e.what(), sizeof(global_last_error)); + return PHONE_STATUS_FAILURE; + } + + return PHONE_STATUS_SUCCESS; +} + +phone_status_t phone_stop_call_waiting(phone_t instance) { + try { + instance->stop_call_waiting(); + } catch (const phone::exception& e) { + strncpy(global_last_error, e.what(), sizeof(global_last_error)); + return PHONE_STATUS_FAILURE; + } + + return PHONE_STATUS_SUCCESS; +} + +unsigned phone_get_call_count(phone_t instance) { + return instance->get_call_count(); +} + +void phone_set_log_function(phone_t instance, void (*fn)(int, const char *, long, const char *)) { + instance->set_log_function([fn](int level, std::string_view message, long thread_id, std::string_view thread_name){ + fn(level, message.data(), thread_id, thread_name.data()); + }); +} + +phone_status_t phone_handle_ip_change(void) { + try { + phone_instance_t::handle_ip_change(); + } catch (const phone::exception& e) { + strncpy(global_last_error, e.what(), sizeof(global_last_error)); + return PHONE_STATUS_FAILURE; + } + + return PHONE_STATUS_SUCCESS; +} \ No newline at end of file diff --git a/src/phone/phone_instance_t.cpp b/src/phone/phone_instance_t.cpp index 646dc81..783f171 100644 --- a/src/phone/phone_instance_t.cpp +++ b/src/phone/phone_instance_t.cpp @@ -1,7 +1,8 @@ #include "phone_instance_t.h" #include "private/account_t.h" -#include "private/nameserver.h" -#include "private/tones.h" +#include "private/system_nameserver.h" +#include "private/tone_generator_helper.h" +#include "private/log_writer_t.h" #include #include @@ -9,33 +10,42 @@ phone_instance_t::phone_instance_t(std::string user_agent, std::vector nameserver, std::vector stunserver) : m_ep{std::make_unique()}, m_account{std::make_unique()} { + m_call_waiting_tone_generator = std::make_unique(); + m_dtmf_tone_generator = std::make_unique(); + pj::EpConfig ep_cfg{}; ep_cfg.uaConfig.userAgent = std::move(user_agent); ep_cfg.uaConfig.nameserver = std::move(nameserver); ep_cfg.uaConfig.stunServer = std::move(stunserver); + // FIXME: hopefully pjsip fixes the assumption about beeing the owner of the *log_writer_t + // https://github.com/pjsip/pjproject/issues/3511 + m_log_writer = new log_writer_t{}; + ep_cfg.logConfig.writer = m_log_writer; + ep_cfg.medConfig.ecOptions = PJMEDIA_ECHO_USE_SW_ECHO; + try { m_ep->libCreate(); m_ep->libInit(ep_cfg); m_ep->libStart(); + + m_call_waiting_tone_generator->createToneGenerator(); + m_call_waiting_tone_generator->startTransmit2(m_ep->audDevManager().getPlaybackDevMedia(), {}); + + m_dtmf_tone_generator->createToneGenerator(); + m_dtmf_tone_generator->startTransmit2(m_ep->audDevManager().getPlaybackDevMedia(), {}); } catch (const pj::Error& e) { throw phone::exception{e.info()}; } - - m_call_waiting_tone_generator = std::make_unique(); - m_call_waiting_tone_generator->createToneGenerator(); - - m_dtmf_tone_generator = std::make_unique(); - m_dtmf_tone_generator->createToneGenerator(); - m_dtmf_tone_generator->startTransmit(m_ep->audDevManager().getPlaybackDevMedia()); } phone_instance_t::phone_instance_t(std::string user_agent, std::vector stunserver) : phone_instance_t(std::move(user_agent), system_nameserver(), std::move(stunserver)) {} phone_instance_t::~phone_instance_t() { - m_dtmf_tone_generator->stopTransmit(m_ep->audDevManager().getPlaybackDevMedia()); + m_dtmf_tone_generator.reset(); + m_call_waiting_tone_generator.reset(); m_ep->libDestroy(); } @@ -165,19 +175,19 @@ void phone_instance_t::hangup_call(std::string call_id) { } } -void phone_instance_t::dtmf(int call_index, const std::string &digits) { +void phone_instance_t::dtmf(int call_index, const std::string &digits) const { try { m_account->dial_dtmf(call_index, digits); - play_tones(m_dtmf_tone_generator.get(), m_ep->audDevManager().getPlaybackDevMedia(), digits); + play_tones(*m_dtmf_tone_generator, digits); } catch (const pj::Error& e) { throw phone::exception{e.info()}; } } -void phone_instance_t::dtmf(std::string call_id, const std::string &digits) { +void phone_instance_t::dtmf(std::string call_id, const std::string &digits) const { try { m_account->dial_dtmf(std::move(call_id), digits); - play_tones(m_dtmf_tone_generator.get(), m_ep->audDevManager().getPlaybackDevMedia(), digits); + play_tones(*m_dtmf_tone_generator, digits); } catch (const pj::Error& e) { throw phone::exception{e.info()}; } @@ -191,7 +201,7 @@ void phone_instance_t::set_log_level(int level) { pj_log_set_level(level); } -std::string phone_instance_t::get_call_id(int call_index) { +std::string phone_instance_t::get_call_id(int call_index) const { try { return m_account->get_call_id(call_index); } catch (const std::invalid_argument& e) { @@ -199,7 +209,7 @@ std::string phone_instance_t::get_call_id(int call_index) { } } -int phone_instance_t::get_call_index(const std::string& call_id) { +int phone_instance_t::get_call_index(const std::string& call_id) const { try { return m_account->get_call_index(call_id); } catch (const std::invalid_argument& e) { @@ -250,7 +260,7 @@ void phone_instance_t::refresh_audio_devices() { pjmedia_aud_dev_refresh(); } -std::optional phone_instance_t::call_incoming_message(int call_index) { +std::optional phone_instance_t::call_incoming_message(int call_index) const { try { return m_account->call_incoming_message(call_index); } catch (const std::invalid_argument& e) { @@ -258,7 +268,7 @@ std::optional phone_instance_t::call_incoming_message(int call_inde } } -std::optional phone_instance_t::call_incoming_message(const std::string& call_id) { +std::optional phone_instance_t::call_incoming_message(const std::string& call_id) const { try { return m_account->call_incoming_message(call_id); } catch (const std::invalid_argument& e) { @@ -266,7 +276,7 @@ std::optional phone_instance_t::call_incoming_message(const std::st } } -std::optional phone_instance_t::call_answer_after(int call_index) { +std::optional phone_instance_t::call_answer_after(int call_index) const { try { return m_account->call_answer_after(call_index); } catch (const std::invalid_argument& e) { @@ -274,7 +284,7 @@ std::optional phone_instance_t::call_answer_after(int call_index) { } } -std::optional phone_instance_t::call_answer_after(const std::string& call_id) { +std::optional phone_instance_t::call_answer_after(const std::string& call_id) const { try { return m_account->call_answer_after(call_id); } catch (const std::invalid_argument& e) { @@ -290,16 +300,44 @@ void phone_instance_t::register_thread(const std::string &name) { } } -bool phone_instance_t::is_thread_registered() { +bool phone_instance_t::is_thread_registered() const { return m_ep->libIsThreadRegistered(); } -void phone_instance_t::play_call_waiting() { - m_call_waiting_tone_generator->startTransmit(m_ep->audDevManager().getPlaybackDevMedia()); - m_call_waiting_tone_generator->play(call_waiting_sequence(), true); +void phone_instance_t::play_call_waiting() const { + try { + m_call_waiting_tone_generator->play(call_waiting_sequence(), true); + } catch (const pj::Error& e) { + throw phone::exception{e.info()}; + } +} + +void phone_instance_t::stop_call_waiting() const { + try { + m_call_waiting_tone_generator->stop(); + } catch (const pj::Error& e) { + throw phone::exception{e.info()}; + } } -void phone_instance_t::stop_call_waiting() { - m_call_waiting_tone_generator->stopTransmit(m_ep->audDevManager().getPlaybackDevMedia()); +int phone_instance_t::get_call_count() { + return m_account->get_call_count(); +} + +void phone_instance_t::set_log_function(const std::function& log_function) { + m_log_writer->log_function = [log_function](int level, std::string_view message, long thread_id, std::string_view thread_name){ + log_function(level, message, thread_id, thread_name); + }; +} + +void phone_instance_t::handle_ip_change() { + pjsua_ip_change_param prm; + pjsua_ip_change_param_default(&prm); + auto status = pjsua_handle_ip_change(&prm); + if (status != PJ_SUCCESS) { + char buffer[PJ_ERR_MSG_SIZE]; + pj_strerror(status, buffer, sizeof(buffer)); + throw phone::exception{buffer}; + } } From 6c26ca4bbc743238b926de156f3ae13569e12b83 Mon Sep 17 00:00:00 2001 From: Oliver Epper Date: Sat, 22 Apr 2023 19:15:42 +0200 Subject: [PATCH 11/12] Java & Python demos --- .../java/de/oliver_epper/libphone/Main.java | 41 ++++++++++ .../java/de/oliver_epper/libphone/Phone.java | 63 ++++++++++++++++ src/python_cli/phone_ctypes.py | 74 ++++++++++++++++--- src/python_cli/python_cli.py | 32 ++++++++ 4 files changed, 201 insertions(+), 9 deletions(-) diff --git a/src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Main.java b/src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Main.java index 419cd52..f6e468b 100644 --- a/src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Main.java +++ b/src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Main.java @@ -20,6 +20,10 @@ public static void main(String[] args) { var registration = executor.submit(register); registration.get(); + phone.setLogFunction((int level, String message, long threadId, String threadName) -> { + System.out.println(message); + }); + phone.registerOnRegistrationStateCallback((isRegistered, registrationState, ctx) -> { if (isRegistered) { System.out.println("phone is registered: " + Phone.describeStatus((registrationState))); @@ -189,6 +193,38 @@ public static void main(String[] args) { e.printStackTrace(); } } + case 'p' -> { + scanner.nextLine(); + System.out.println("Please enter call index: "); + var callIndex = scanner.nextInt(); + scanner.nextLine(); + System.out.println("Please enter desired DTMF digits: "); + var digits = scanner.nextLine(); + try { + phone.dtmf(callIndex, digits); + } catch (PhoneException e) { + e.printStackTrace(); + } + } + case 'b' -> { + System.out.println("Play call waiting"); + phone.playCallWaiting(); + } + case 'B' -> { + System.out.println("Stop call waiting"); + phone.stopCallWaiting(); + } + case '#' -> { + System.out.println("Call count: " + phone.callCount()); + } + case 'i' -> { + System.out.println("Handle IP change"); + try { + phone.handleIpChange(); + } catch (PhoneException e) { + e.printStackTrace(); + } + } default -> System.out.println(""" c - call a number C - call Time Announcement of Telekom Germany @@ -200,6 +236,11 @@ public static void main(String[] args) { l - change log level d - list audio devices D - change audio devices + p - play DTMF + b - play call waiting + B - stop call waiting + # - call count + i - handle IP change q - quit """); } diff --git a/src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Phone.java b/src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Phone.java index 2c92f74..5cebe3b 100644 --- a/src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Phone.java +++ b/src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Phone.java @@ -56,6 +56,10 @@ interface CallStateIdCallback extends Callback { void onCallStateIdCallback(String callId, int state, Pointer ctx); } + interface LogFunction extends Callback { + void logFunction(int level, String message, long threadId, String threadName); + } + // PHONE_EXPORT phone_t phone_create(const char *user_agent, const char * const nameserver[], size_t nameserver_count, const char * const stunserver[], size_t stunserver_count); Pointer phone_create(String userAgent, String[] nameServers, NativeLong nameServerLength, String[] stunServers, NativeLong stunServerLength); @@ -105,6 +109,12 @@ interface CallStateIdCallback extends Callback { int phone_hangup_call_index(Pointer instance, int call_index); // PHONE_EXPORT phone_status_t phone_hangup_call_id(phone_t instance, const char *call_id); int phone_hangup_call_id(Pointer instance, String callId); + + // PHONE_EXPORT phone_status_t phone_play_dtmf_call_index(phone_t instance, int call_index, const char *digits); + int phone_play_dtmf_call_index(Pointer instance, int call_index, String digits); + // PHONE_EXPORT phone_status_t phone_play_dtmf_call_id(phone_t instance, const char *call_id, const char *digits); + int phone_play_dtmf_call_id(Pointer instance, String call_id, String digits); + // PHONE_EXPORT void phone_hangup_calls(phone_t instance); void phone_hangup_calls(Pointer instance); @@ -145,12 +155,27 @@ interface CallStateIdCallback extends Callback { // PHONE_EXPORT void phone_set_log_level(int level); void phone_set_log_level(int level); + // PHONE_EXPORT void phone_set_log_function(phone_t instance, void (*fn)(int level, const char *message, long thread_id, const char *thread_name)); + void phone_set_log_function(Pointer instance, LogFunction logFunction); + // PHONE_EXPORT phone_status_t phone_register_thread(phone_t instance, const char *name); int phone_register_thread(Pointer instance, String name); // PHONE_EXPORT int phone_is_thread_registered(phone_t instance); boolean phone_is_thread_registered(Pointer instance); + // PHONE_EXPORT phone_status_t phone_play_call_waiting(phone_t instance); + int phone_play_call_waiting(Pointer instance); + // PHONE_EXPORT phone_status_t phone_stop_call_waiting(phone_t instance); + int phone_stop_call_waiting(Pointer instance); + + // PHONE_EXPORT unsigned phone_get_call_count(phone_t instance); + int phone_get_call_count(Pointer instance); + + // PHONE_EXPORT phone_status_t phone_handle_ip_change(void); + int phone_handle_ip_change(); + + class AudioDeviceInfo extends Structure { public int id; public Pointer driver; @@ -236,6 +261,10 @@ void call(String number) throws PhoneException { } } + void setLogFunction(PhoneLibrary.LogFunction logFunction) { + CPHONE.phone_set_log_function(phone, logFunction); + } + void registerOnRegistrationStateCallback(PhoneLibrary.RegistrationStateCallback cb) { CPHONE.phone_register_on_registration_state_callback(phone, cb, null); } @@ -268,6 +297,18 @@ void hangup(String callId) throws PhoneException { } } + void dtmf(int callIndex, String digits) throws PhoneException { + if (CPHONE.phone_play_dtmf_call_index(phone, callIndex, digits) != PHONE_STATUS_SUCCESS) { + throw new PhoneException(lastError()); + } + } + + void dtmf(String callId, String digits) throws PhoneException { + if (CPHONE.phone_play_dtmf_call_id(phone, callId, digits) != PHONE_STATUS_SUCCESS) { + throw new PhoneException(lastError()); + } + } + void hangupCalls() { CPHONE.phone_hangup_calls(phone); } @@ -373,4 +414,26 @@ void registerThread(String name) { boolean isThreadRegistered() { return CPHONE.phone_is_thread_registered(phone); } + + void playCallWaiting() throws PhoneException { + if (CPHONE.phone_play_call_waiting(phone) != PHONE_STATUS_SUCCESS) { + throw new PhoneException(lastError()); + } + } + + void stopCallWaiting() throws PhoneException { + if (CPHONE.phone_stop_call_waiting(phone) != PHONE_STATUS_SUCCESS) { + throw new PhoneException(lastError()); + } + } + + int callCount() { + return CPHONE.phone_get_call_count(phone); + } + + void handleIpChange() throws PhoneException { + if (CPHONE.phone_handle_ip_change() != PHONE_STATUS_SUCCESS) { + throw new PhoneException(lastError()); + } + } } diff --git a/src/python_cli/phone_ctypes.py b/src/python_cli/phone_ctypes.py index e8d0d3c..b73d21a 100644 --- a/src/python_cli/phone_ctypes.py +++ b/src/python_cli/phone_ctypes.py @@ -181,6 +181,36 @@ def phone_answer_call_id(phone, call_id): __phone_hangup_call_index.argtypes = [c_void_p, c_int] +# PHONE_EXPORT phone_status_t phone_hangup_call_id(phone_t instance, const char *call_id); +def phone_hangup_call_id(phone, call_id): + __phone_hangup_call_id = libphone.phone_hangup_call_id + __phone_hangup_call_id.restype = c_int + __phone_hangup_call_id.argtypes = [c_void_p, c_char_p] + c_call_id = c_char_p(call_id.encode('utf-8')) + return __phone_hangup_call_id(phone, c_call_id) + + +# PHONE_EXPORT phone_status_t phone_play_dtmf_call_index(phone_t instance, int call_index, const char *digits); +def phone_play_dtmf_call_index(phone, call_index, digits): + __phone_play_dtmf_call_index = libphone.phone_play_dtmf_call_index + __phone_play_dtmf_call_index.restype = c_int + __phone_play_dtmf_call_index.argtypes = [c_void_p, c_int, c_char_p] + c_digits = c_char_p(digits.encode('utf-8')) + if __phone_play_dtmf_call_index(phone, call_index, c_digits) != PHONE_STATUS_SUCCESS: + raise Exception(phone_last_error()) + + +# PHONE_EXPORT phone_status_t phone_play_dtmf_call_id(phone_t instance, const char *call_id, const char *digits); +def phone_play_dtmf_call_id(phone, call_id, digits): + __phone_play_dtmf_call_id = libphone.phone_play_dtmf_call_id + __phone_play_dtmf_call_id.restype = c_int + __phone_play_dtmf_call_id.argtypes = [c_void_p, c_char_p, c_char_p] + c_call_id = c_char_p(call_id.encode('utf-8')) + c_digits = c_char_p(digits.encode('utf-8')) + if __phone_play_dtmf_call_id(phone, c_call_id, c_digits) != PHONE_STATUS_SUCCESS: + raise Exception(phone_last_error()) + + # PHONE_EXPORT phone_status_t phone_start_ringing_call_index(phone_t instance, int call_index); phone_start_ringing_call_index = libphone.phone_start_ringing_call_index phone_start_ringing_call_index.restype = c_int @@ -196,15 +226,6 @@ def phone_start_ringing_call_id(phone, call_id): return __phone_start_ringing_call_id(phone, c_call_id) -# PHONE_EXPORT phone_status_t phone_hangup_call_id(phone_t instance, const char *call_id); -def phone_hangup_call_id(phone, call_id): - __phone_hangup_call_id = libphone.phone_hangup_call_id - __phone_hangup_call_id.restype = c_int - __phone_hangup_call_id.argtypes = [c_void_p, c_char_p] - c_call_id = c_char_p(call_id.encode('utf-8')) - return __phone_hangup_call_id(phone, c_call_id) - - # PHONE_DEPRECATED_EXPORT phone_status_t phone_hangup_call(phone_t instance, int call_id); phone_hangup_call = __phone_hangup_call_index @@ -430,6 +451,35 @@ def phone_git_description(): __phone_git_description(buffer, len(buffer)) return buffer.value.decode('utf-8') +# PHONE_EXPORT void phone_set_log_function(phone_t instance, void (*fn)(int level, const char *message, long thread_id, const char *thread_name)); +phone_set_log_function = libphone.phone_set_log_function +phone_set_log_function.restype = None +phone_set_log_function.argtypes = [c_void_p, c_void_p] + + +# PHONE_EXPORT phone_status_t phone_play_call_waiting(phone_t instance); +phone_play_call_waiting = libphone.phone_play_call_waiting +phone_play_call_waiting.restype = None +phone_play_call_waiting.argtypes = [c_void_p] + + +# PHONE_EXPORT phone_status_t phone_stop_call_waiting(phone_t instance); +phone_stop_call_waiting = libphone.phone_stop_call_waiting +phone_stop_call_waiting.restype = None +phone_stop_call_waiting.argtypes = [c_void_p] + + +# PHONE_EXPORT unsigned phone_get_call_count(phone_t instance); +phone_get_call_count = libphone.phone_get_call_count +phone_get_call_count.restype = c_int +phone_get_call_count.argtypes = [c_void_p] + + +# PHONE_EXPORT phone_status_t phone_handle_ip_change(void); +phone_handle_ip_change = libphone.phone_handle_ip_change +phone_handle_ip_change.restype = None +phone_handle_ip_change.argtypes = [c_void_p] + def die(instance): phone_destroy(instance) @@ -448,5 +498,11 @@ def die(instance): l - change log level d - list audio devices D - change audio devices +p - play DTMF in call +P - play DTMF in call +b - play call waiting +B - stop call waiting +# - print call count +i - handle ip change q - quit ''' diff --git a/src/python_cli/python_cli.py b/src/python_cli/python_cli.py index 99048ef..363dc25 100644 --- a/src/python_cli/python_cli.py +++ b/src/python_cli/python_cli.py @@ -31,6 +31,11 @@ phone_set_log_level(0) +@CFUNCTYPE(None, c_int, c_char_p, c_long, c_char_p) +def log_function(level, message, thread_id, thread_name): + print(f"-> {message.decode('utf-8')}", end='') + + # callbacks @CFUNCTYPE(None, c_int, c_int, c_void_p) def on_registration_state_cb(is_registered, registration_state, ctx): @@ -82,6 +87,7 @@ def on_call_state_id_cb(call_id, state, ctx): print(f"Call id: {call_id.decode('utf-8')} and index: {phone_get_call_index(phone, call_id)} – state: {phone_call_state_name(state)}") +phone_set_log_function(phone, log_function) phone_register_on_registration_state_callback(phone, on_registration_state_cb, None) phone_register_on_incoming_call_index_callback(phone, on_incoming_call_index_cb, None) phone_register_on_incoming_call_id_callback(phone, on_incoming_call_id_cb, None) @@ -154,6 +160,32 @@ def on_call_state_id_cb(call_id, state, ctx): playback_device = int(input("please enter desired playback device: ")) if phone_set_audio_devices(capture_device, playback_device) != PHONE_STATUS_SUCCESS: print(phone_last_error(), file=sys.stderr) + elif command == 'p': + call_index = int(input("please enter call index: ")) + digits = input("please enter DTMF digits: ") + try: + phone_play_dtmf_call_index(phone, call_index, digits) + except Exception as e: + print(e) + elif command == 'P': + call_id = input("Please enter call id: ") + digits = input("Please enter DTMF digits: ") + try: + phone_play_dtmf_call_id(phone, call_id, digits) + except Exception as e: + print(e) + elif command == 'b': + print("play call waiting") + phone_play_call_waiting(phone) + elif command == 'B': + print("stop call waiting") + phone_stop_call_waiting(phone) + elif command == '#': + print(f"number of calls: {phone_get_call_count(phone)}") + elif command == 'i': + print("handling ip change") + phone_handle_ip_change(phone) + print("shutting down...") phone_destroy(phone) From 12e341fcad7cb50dd0d6de13c68f548b22d7f28c Mon Sep 17 00:00:00 2001 From: Oliver Epper Date: Mon, 24 Apr 2023 11:16:53 +0200 Subject: [PATCH 12/12] README update --- README.md | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b2e48eb..f032e47 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # libphone -libphone is a library that sits on top of [PJSIP project](https://github.com/pjsip/pjproject) and tries to make it very simple to build a softphone. libphone provides a C++ and a C-API. Included with the library is a Python script that demonstrates the API [python_cli.py](src/python_cli/python_cli.py). You can download one of the [binaries](#binaries) and just enter your SIP credentials if you want to test things out. +libphone is a library that sits on top of [PJSIP project](https://github.com/pjsip/pjproject) and tries to make it very simple to build a softphone. libphone provides a C++ and a C-API. Included with the library is a Python script that demonstrates the API [python_cli.py](src/python_cli/python_cli.py). You can download one of the [binaries](#binaries) and just enter your SIP credentials if you want to test things out. Included with the library you can also find usage example in C, C++ and Java using the C-API via JNA. + ## Overview @@ -14,11 +15,14 @@ libphone is a library that sits on top of [PJSIP project](https://github.com/pjs * [1und1 configuration](#1und1-configuration) * [Handling of audio devices](#handling-of-audio-devices) * [Call-Info answer-after](#call-info-answer-after) + * [DTMF](#dtmf) + * [Handle IP address change](#handle-ip-address-change) * [Binaries](#binaries) * [Build instructions for Linux](#build-instructions-for-linux) * [Build instructions for macOS](#build-instructions-for-macos) + ## Usage You can create a phone with @@ -27,7 +31,14 @@ You can create a phone with phone = phone_create("☎️", ["194.25.0.69","194.25.0.79"], "stun.t-online.de") ``` -and connect the phone to your server via: +Starting with libphone-0.7.0 there's an additional API `phone_create_with_system_nameserver` which gives you three +possibilities to configure the system: + +1. use getaddr resolving of hostnames. If you want the behavior give the system an empty array of nameservers. +2. use the nameservers of the system for SRV lookups. +3. provide the nameservers for SRV lookups. + +Once created connect the phone to your server via: ```python phone_connect(phone, "tel.t-online.de", "your_sip_username", "your_sip_password") @@ -48,6 +59,8 @@ phone_register_on_call_state_callback(phone, on_call_state, None) The third parameter to the callback functions is required if you want to use the C-API with an object-oriented programming language like Swift: +Since 0.6.0 there is a callback for the registration state and since 0.7.0 you can register your own log function. + ```swift phone_register_on_incoming_call_callback(phone, { callId, ctx in guard let ctx else { return } @@ -177,6 +190,14 @@ if answer_after >= 0: print(f"will auto answer call after {answer_after} seconds") ``` +## DTMF +Since 0.7.0 you can send (and play) DTMF digits in a call. The sending is done via RFC2833. Sound is played locally to the user to provide auditory feedback. + + +## Handle IP address change +Since 0.7.0 there is an API call `phone_handle_ip_change`. + + ## Binaries I maintain binaries for macOS (signed, notarized and ready to go) and Ubuntu. You can get them from the GitHub release page, or my website [libphone](https://oliver-epper.de/apps/libphone/). @@ -259,8 +280,15 @@ cmake --build build-libphone --config Release --target install ## Build instructions for macOS -You can use my already prebuild package `pjproject-apple-platforms`. Install it with the following commands: +Begining with libphone-0.7.0 there is a shell-script `build-darwin-dependencies.sh` which should build all required dependencies (opus, sdl, pjproject) for: + +- iOS +- iOS simulator running on arm64 and x86_64 +- catalyst running on arm64 and x86_64 +- macOS running on arm64 and x86_64 +- macOS universal +For a quick test the old way does still work: ```shell brew tap oliverepper/made brew install pjproject-apple-platforms