Skip to content

Commit

Permalink
Merge branch 'release/0.7.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverepper committed Apr 24, 2023
2 parents 199e923 + 12e341f commit 6c106a2
Show file tree
Hide file tree
Showing 22 changed files with 705 additions and 61 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
34 changes: 31 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
<!-- TOC -->
Expand All @@ -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)
<!-- TOC -->


## Usage

You can create a phone with
Expand All @@ -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")
Expand All @@ -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 }
Expand Down Expand Up @@ -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/).
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion scripts/build-pjproject-catalyst-arm64.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash
# Oliver Epper <[email protected]>

source "$(dirname $0)"/build-pjproject-darwin-base.sh
source "$(dirname "$0")"/build-pjproject-darwin-base.sh

PREFIX="${PREFIX}/catalyst-arm64"
rm -rf "${PREFIX}"
Expand Down
31 changes: 29 additions & 2 deletions src/c_cli/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,24 @@ 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;
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);

// 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);
Expand Down Expand Up @@ -235,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;
Expand Down
52 changes: 46 additions & 6 deletions src/cpp_cli/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -77,21 +77,39 @@ 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 = "";
}
}
};

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"}
}};

// 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);
Expand All @@ -102,8 +120,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();
Expand Down Expand Up @@ -171,6 +193,24 @@ 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);
}
} 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();
} 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;
Expand Down
2 changes: 1 addition & 1 deletion src/cpp_cli/simple_task_system.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<void()> f;
Expand Down
46 changes: 42 additions & 4 deletions src/java_cli/Cli/src/main/java/de/oliver_epper/libphone/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,23 @@ 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");

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)));
Expand Down Expand Up @@ -192,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
Expand All @@ -203,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
""");
}
Expand Down
Loading

0 comments on commit 6c106a2

Please sign in to comment.