From 991f2f0411b1d907aa667236bf9b28a0024bbc59 Mon Sep 17 00:00:00 2001 From: Victor Gaydov Date: Mon, 15 Jul 2024 22:11:21 +0400 Subject: [PATCH] docs: Update API usage examples --- docs/sphinx/api/examples.rst | 69 +++++++-------- ...lseaudio.c => basic_receiver_pulseaudio.c} | 23 ++++- ...pulseaudio.c => basic_sender_pulseaudio.c} | 23 ++++- .../examples/basic_sender_sine_wave.c | 21 +++++ src/public_api/examples/misc_logging.c | 84 +++++++++++++++++++ ...manipulation.c => misc_uri_manipulation.c} | 2 +- src/public_api/examples/misc_version.c | 55 ++++++++++++ .../{send_receive_rtp.c => send_recv_rtp.c} | 34 ++++---- ...th_fecframe.c => send_recv_rtp_rtcp_fec.c} | 43 +++++++++- 9 files changed, 299 insertions(+), 55 deletions(-) rename src/public_api/examples/{basic_receiver_to_pulseaudio.c => basic_receiver_pulseaudio.c} (88%) rename src/public_api/examples/{basic_sender_from_pulseaudio.c => basic_sender_pulseaudio.c} (88%) create mode 100644 src/public_api/examples/misc_logging.c rename src/public_api/examples/{uri_manipulation.c => misc_uri_manipulation.c} (99%) create mode 100644 src/public_api/examples/misc_version.c rename src/public_api/examples/{send_receive_rtp.c => send_recv_rtp.c} (86%) rename src/public_api/examples/{send_receive_rtp_with_fecframe.c => send_recv_rtp_rtcp_fec.c} (85%) diff --git a/docs/sphinx/api/examples.rst b/docs/sphinx/api/examples.rst index 02c93c546..ca7b975b8 100644 --- a/docs/sphinx/api/examples.rst +++ b/docs/sphinx/api/examples.rst @@ -5,57 +5,60 @@ Examples :local: :depth: 2 -Basic sender and receiver -------------------------- +Basic senders and receivers +--------------------------- * `basic_sender_sine_wave.c `_ - Basic sender example. + Minimal sender: + - creates a sender and connects it to remote address + - generates a 10-second beep and writes it to the sender - This example creates a sender and connects it to remote receiver. - Then it generates a 10-second beep and writes it to the sender. +* `basic_sender_pulseaudio.c `_ -* `basic_sender_from_pulseaudio.c `_ + Another minimal sender: + - creates a sender and connects it to remote address + - captures audio stream from PulseAudio and writes it to the sender - Basic sender example. +* `basic_receiver_pulseaudio.c `_ - This example creates a sender and connects it to remote receiver. - Then it records audio stream from PulseAudio and writes it to the sender. + Minimal receiver: + - creates a receiver and binds it to a local address + - reads audio stream from the receiver and plays it using PulseAudio -* `basic_receiver_to_pulseaudio.c `_ - - Basic receiver example. - - This example creates a receiver and binds it to a known address. - Then it reads audio stream from the receiver and plays it using PulseAudio. - -Network protocols +Network endpoints ----------------- -* `send_receive_rtp.c `_ - - Send and receive samples using bare RTP. +* `send_recv_rtp.c `_ - This example creates a receiver and binds it to an RTP endpoint. - Then it creates a sender and connects it to the receiver endpoint. - Then it starts writing audio stream to the sender and reading it from receiver. + Sending and receiving using bare RTP without extensions: + - creates a receiver and binds it to a single RTP endpoint + - creates a sender and connects it to the receiver endpoint + - one thread writes audio stream to the sender + - another thread reads audio stream from receiver -* `send_receive_rtp_with_fecframe.c `_ +* `send_recv_rtp_rtcp_fec.c `_ - Send and receive samples using RTP and FECFRAME. + Sending a receiving using RTP + FECFRAME + RTCP. - This example is like `send_receive_rtp.c`, but it creates two endpoints: - - the first, source endpoint is used to transmit audio stream - - the second, repair endpoint is used to transmit redundant stream + This example is like ``send_recv_rtp.c``, but it uses three endpoints (on both sender and receiver): + - source endpoint is used to transmit audio stream (via RTP) + - repair endpoint is used to transmit redundant stream for loss recovery (via FECFRAME) + - control endpoint is used to transmit bi-directional control traffic (via RTCP) - The redundant stream is used on receiver to recover lost audio packets. - This is useful on unreliable networks. + This is the recommended way to use the library as it unlocks all the available features like loss recovery, feedback, metrics, etc. Miscellaneous ------------- -* `uri_manipulation.c `_ +* `misc_uri_manipulation.c `_ + + Demonstrates how to build endpoint URI and access its individual parts using ``roc_endpoint`` API. + +* `misc_logging.c `_ + + Demonstrates how to configure log level and handler. - URI manipulation example. +* `misc_version.c `_ - This example demonstrates how to build endpoint URI and access its individual parts. + Demonstrates how to check library version at compile-time and run-time. diff --git a/src/public_api/examples/basic_receiver_to_pulseaudio.c b/src/public_api/examples/basic_receiver_pulseaudio.c similarity index 88% rename from src/public_api/examples/basic_receiver_to_pulseaudio.c rename to src/public_api/examples/basic_receiver_pulseaudio.c index d421c8208..c4a6b1ffb 100644 --- a/src/public_api/examples/basic_receiver_to_pulseaudio.c +++ b/src/public_api/examples/basic_receiver_pulseaudio.c @@ -4,7 +4,7 @@ * Then it reads audio stream from the receiver and plays it using PulseAudio. * * Building: - * cc basic_receiver_to_pulseaudio.c -lroc -lpulse-simple + * cc basic_receiver_pulseaudio.c -lroc -lpulse-simple * * Running: * ./a.out @@ -28,6 +28,7 @@ #define MY_RECEIVER_IP "0.0.0.0" #define MY_RECEIVER_SOURCE_PORT 10101 #define MY_RECEIVER_REPAIR_PORT 10102 +#define MY_RECEIVER_CONTROL_PORT 10103 /* Signal parameters. */ #define MY_SAMPLE_RATE 44100 @@ -123,6 +124,26 @@ int main() { oops(); } + /* Bind receiver to the control (RTCP) packets endpoint. */ + roc_endpoint* control_endp = NULL; + if (roc_endpoint_allocate(&control_endp) != 0) { + oops(); + } + + roc_endpoint_set_protocol(control_endp, ROC_PROTO_RTCP); + roc_endpoint_set_host(control_endp, MY_RECEIVER_IP); + roc_endpoint_set_port(control_endp, MY_RECEIVER_CONTROL_PORT); + + if (roc_receiver_bind(receiver, ROC_SLOT_DEFAULT, ROC_INTERFACE_AUDIO_CONTROL, + control_endp) + != 0) { + oops(); + } + + if (roc_endpoint_deallocate(control_endp) != 0) { + oops(); + } + /* Initialize PulseAudio parameters. */ pa_sample_spec sample_spec; memset(&sample_spec, 0, sizeof(sample_spec)); diff --git a/src/public_api/examples/basic_sender_from_pulseaudio.c b/src/public_api/examples/basic_sender_pulseaudio.c similarity index 88% rename from src/public_api/examples/basic_sender_from_pulseaudio.c rename to src/public_api/examples/basic_sender_pulseaudio.c index 05e11b0a2..714e25111 100644 --- a/src/public_api/examples/basic_sender_from_pulseaudio.c +++ b/src/public_api/examples/basic_sender_pulseaudio.c @@ -4,7 +4,7 @@ * Then it records audio stream from PulseAudio and writes it to the sender. * * Building: - * cc basic_sender_sine_wave.c -lroc + * cc basic_sender_pulseaudio.c -lroc * * Running: * ./a.out @@ -29,6 +29,7 @@ #define MY_RECEIVER_IP "127.0.0.1" #define MY_RECEIVER_SOURCE_PORT 10101 #define MY_RECEIVER_REPAIR_PORT 10102 +#define MY_RECEIVER_CONTROL_PORT 10103 /* Signal parameters */ #define MY_SAMPLE_RATE 44100 @@ -124,6 +125,26 @@ int main() { oops(); } + /* Connect sender to the receiver control (RTCP) packets endpoint. */ + roc_endpoint* control_endp = NULL; + if (roc_endpoint_allocate(&control_endp) != 0) { + oops(); + } + + roc_endpoint_set_protocol(control_endp, ROC_PROTO_RTCP); + roc_endpoint_set_host(control_endp, MY_RECEIVER_IP); + roc_endpoint_set_port(control_endp, MY_RECEIVER_CONTROL_PORT); + + if (roc_sender_connect(sender, ROC_SLOT_DEFAULT, ROC_INTERFACE_AUDIO_CONTROL, + control_endp) + != 0) { + oops(); + } + + if (roc_endpoint_deallocate(control_endp) != 0) { + oops(); + } + /* Initialize PulseAudio parameters. */ pa_sample_spec sample_spec; memset(&sample_spec, 0, sizeof(sample_spec)); diff --git a/src/public_api/examples/basic_sender_sine_wave.c b/src/public_api/examples/basic_sender_sine_wave.c index ca8d534c5..4398f4e66 100644 --- a/src/public_api/examples/basic_sender_sine_wave.c +++ b/src/public_api/examples/basic_sender_sine_wave.c @@ -27,6 +27,7 @@ #define MY_RECEIVER_IP "127.0.0.1" #define MY_RECEIVER_SOURCE_PORT 10101 #define MY_RECEIVER_REPAIR_PORT 10102 +#define MY_RECEIVER_CONTROL_PORT 10103 /* Signal parameters */ #define MY_SAMPLE_RATE 44100 @@ -138,6 +139,26 @@ int main() { oops(); } + /* Connect sender to the receiver control (RTCP) packets endpoint. */ + roc_endpoint* control_endp = NULL; + if (roc_endpoint_allocate(&control_endp) != 0) { + oops(); + } + + roc_endpoint_set_protocol(control_endp, ROC_PROTO_RTCP); + roc_endpoint_set_host(control_endp, MY_RECEIVER_IP); + roc_endpoint_set_port(control_endp, MY_RECEIVER_CONTROL_PORT); + + if (roc_sender_connect(sender, ROC_SLOT_DEFAULT, ROC_INTERFACE_AUDIO_CONTROL, + control_endp) + != 0) { + oops(); + } + + if (roc_endpoint_deallocate(control_endp) != 0) { + oops(); + } + /* Generate sine wave and write it to the sender. */ size_t i; for (i = 0; i < MY_SINE_DURATION / MY_BUFFER_SIZE; i++) { diff --git a/src/public_api/examples/misc_logging.c b/src/public_api/examples/misc_logging.c new file mode 100644 index 000000000..60754b138 --- /dev/null +++ b/src/public_api/examples/misc_logging.c @@ -0,0 +1,84 @@ +/* Logging configuration example. + * + * This example demonstrates how to configure logging. + * + * Building: + * cc misc_logging.c -lroc + * + * Running: + * ./a.out + * + * License: + * public domain + */ + +#include +#include +#include + +#include +#include + +#define oops() \ + do { \ + fprintf(stderr, "oops: failure on %s:%d\n", __FILE__, __LINE__); \ + fprintf(stderr, "exiting!\n"); \ + exit(1); \ + } while (0) + +static void my_log_handler(const roc_log_message* message, void* argument) { + const char* lvl = NULL; + + switch (message->level) { + case ROC_LOG_ERROR: + lvl = "ERROR"; + break; + + case ROC_LOG_INFO: + lvl = "INFO"; + break; + + case ROC_LOG_NOTE: + lvl = "NOTE"; + break; + + case ROC_LOG_DEBUG: + lvl = "DEBUG"; + break; + + case ROC_LOG_TRACE: + lvl = "TRACE"; + break; + + default: + lvl = "UNKNOWN"; + break; + } + + printf("level=%s module=%s time=%lld pid=%llu tid=%llu text=%s\n", lvl, + message->module, message->time, message->pid, message->tid, message->text); +} + +int main() { + /* Allow all log message starting from DEBUG level and higher. + */ + roc_log_set_level(ROC_LOG_DEBUG); + + /* Set custom handler for log messages. + */ + roc_log_set_handler(my_log_handler, NULL); + + /* Do something to trigger some logging. + */ + roc_context_config context_config; + memset(&context_config, 0, sizeof(context_config)); + roc_context* context = NULL; + if (roc_context_open(&context_config, &context) != 0) { + oops(); + } + if (roc_context_close(context) != 0) { + oops(); + } + + return 0; +} diff --git a/src/public_api/examples/uri_manipulation.c b/src/public_api/examples/misc_uri_manipulation.c similarity index 99% rename from src/public_api/examples/uri_manipulation.c rename to src/public_api/examples/misc_uri_manipulation.c index 5058be865..ed0a77ce7 100644 --- a/src/public_api/examples/uri_manipulation.c +++ b/src/public_api/examples/misc_uri_manipulation.c @@ -3,7 +3,7 @@ * This example demonstrates how to build endpoint URI and access its individual parts. * * Building: - * cc uri_manipulation.c -lroc + * cc misc_uri_manipulation.c -lroc * * Running: * ./a.out diff --git a/src/public_api/examples/misc_version.c b/src/public_api/examples/misc_version.c new file mode 100644 index 000000000..5046c334c --- /dev/null +++ b/src/public_api/examples/misc_version.c @@ -0,0 +1,55 @@ +/* Logging configuration example. + * + * This example demonstrates how to check library version. + * + * Building: + * cc misc_version.c -lroc + * + * Running: + * ./a.out + * + * License: + * public domain + */ + +#include + +#include + +int main() { + /* Inspect compile-time library version, defined in header file. + */ + printf("compile-time version: %u.%u.%u, version code: %u\n", ROC_VERSION_MAJOR, + ROC_VERSION_MINOR, ROC_VERSION_PATCH, ROC_VERSION); + +#if ROC_VERSION >= ROC_VERSION_CODE(0, 3, 0) + printf("compile-time version is >= 0.3.0\n"); +#else + printf("compile-time version is < 0.3.0\n"); +#endif + + /* Inspect run-time library version, returned by a function. + */ + roc_version version; + roc_version_load(&version); + + printf("run-time version: %u.%u.%u, version code: %u\n", version.major, version.minor, + version.patch, version.code); + + if (version.code >= ROC_VERSION_CODE(0, 3, 0)) { + printf("run-time version is >= 0.3.0\n"); + } else { + printf("run-time version is < 0.3.0\n"); + } + + /* Compare compile-time and run-time version. + * They may differ when using a shared library. + */ + if (version.code == ROC_VERSION) { + printf("compile-time and run-time versions match\n"); + } else { + printf("compile-time and run-time versions differ\n"); + } + + return 0; +} diff --git a/src/public_api/examples/send_receive_rtp.c b/src/public_api/examples/send_recv_rtp.c similarity index 86% rename from src/public_api/examples/send_receive_rtp.c rename to src/public_api/examples/send_recv_rtp.c index ed9202fb8..8d4dee275 100644 --- a/src/public_api/examples/send_receive_rtp.c +++ b/src/public_api/examples/send_recv_rtp.c @@ -5,7 +5,7 @@ * Then it starts writing audio stream to the sender and reading it from receiver. * * Building: - * cc send_receive_rtp.c -lroc + * cc send_recv_rtp.c -lroc * * Running: * ./a.out @@ -27,8 +27,7 @@ /* Receiver parameters. */ #define MY_RECEIVER_IP "127.0.0.1" -#define MY_RECEIVER_SOURCE_PORT 10201 -#define MY_RECEIVER_REPAIR_PORT 10202 +#define MY_RECEIVER_PORT 10201 /* Signal parameters */ #define MY_SAMPLE_RATE 44100 @@ -61,22 +60,22 @@ static void* receiver_loop(void* arg) { /* Bind receiver to the source (audio) packets endpoint. * The receiver will expect packets with RTP header on this port. */ - roc_endpoint* source_endp = NULL; - if (roc_endpoint_allocate(&source_endp) != 0) { + roc_endpoint* rtp_endp = NULL; + if (roc_endpoint_allocate(&rtp_endp) != 0) { oops(); } - roc_endpoint_set_protocol(source_endp, ROC_PROTO_RTP); - roc_endpoint_set_host(source_endp, MY_RECEIVER_IP); - roc_endpoint_set_port(source_endp, MY_RECEIVER_SOURCE_PORT); + roc_endpoint_set_protocol(rtp_endp, ROC_PROTO_RTP); + roc_endpoint_set_host(rtp_endp, MY_RECEIVER_IP); + roc_endpoint_set_port(rtp_endp, MY_RECEIVER_PORT); if (roc_receiver_bind(receiver, ROC_SLOT_DEFAULT, ROC_INTERFACE_AUDIO_SOURCE, - source_endp) + rtp_endp) != 0) { oops(); } - if (roc_endpoint_deallocate(source_endp) != 0) { + if (roc_endpoint_deallocate(rtp_endp) != 0) { oops(); } @@ -141,22 +140,21 @@ static void* sender_loop(void* arg) { /* Connect sender to the receiver source (audio) packets endpoint. * The receiver should expect packets with RTP header on that port. */ - roc_endpoint* source_endp = NULL; - if (roc_endpoint_allocate(&source_endp) != 0) { + roc_endpoint* rtp_endp = NULL; + if (roc_endpoint_allocate(&rtp_endp) != 0) { oops(); } - roc_endpoint_set_protocol(source_endp, ROC_PROTO_RTP); - roc_endpoint_set_host(source_endp, MY_RECEIVER_IP); - roc_endpoint_set_port(source_endp, MY_RECEIVER_SOURCE_PORT); + roc_endpoint_set_protocol(rtp_endp, ROC_PROTO_RTP); + roc_endpoint_set_host(rtp_endp, MY_RECEIVER_IP); + roc_endpoint_set_port(rtp_endp, MY_RECEIVER_PORT); - if (roc_sender_connect(sender, ROC_SLOT_DEFAULT, ROC_INTERFACE_AUDIO_SOURCE, - source_endp) + if (roc_sender_connect(sender, ROC_SLOT_DEFAULT, ROC_INTERFACE_AUDIO_SOURCE, rtp_endp) != 0) { oops(); } - if (roc_endpoint_deallocate(source_endp) != 0) { + if (roc_endpoint_deallocate(rtp_endp) != 0) { oops(); } diff --git a/src/public_api/examples/send_receive_rtp_with_fecframe.c b/src/public_api/examples/send_recv_rtp_rtcp_fec.c similarity index 85% rename from src/public_api/examples/send_receive_rtp_with_fecframe.c rename to src/public_api/examples/send_recv_rtp_rtcp_fec.c index 1ed683d1f..e03a615b6 100644 --- a/src/public_api/examples/send_receive_rtp_with_fecframe.c +++ b/src/public_api/examples/send_recv_rtp_rtcp_fec.c @@ -8,7 +8,7 @@ * This is useful on unreliable networks. * * Building: - * cc send_receive_rtp_with_fecframe.c -lroc + * cc send_recv_rtp_rtcp_fec.c -lroc * * Running: * ./a.out @@ -32,6 +32,7 @@ #define MY_RECEIVER_IP "127.0.0.1" #define MY_RECEIVER_SOURCE_PORT 10201 #define MY_RECEIVER_REPAIR_PORT 10202 +#define MY_RECEIVER_CONTROL_PORT 10203 /* Signal parameters */ #define MY_SAMPLE_RATE 44100 @@ -106,6 +107,26 @@ static void* receiver_loop(void* arg) { oops(); } + /* Bind receiver to the control (RTCP) packets endpoint. */ + roc_endpoint* control_endp = NULL; + if (roc_endpoint_allocate(&control_endp) != 0) { + oops(); + } + + roc_endpoint_set_protocol(control_endp, ROC_PROTO_RTCP); + roc_endpoint_set_host(control_endp, MY_RECEIVER_IP); + roc_endpoint_set_port(control_endp, MY_RECEIVER_CONTROL_PORT); + + if (roc_receiver_bind(receiver, ROC_SLOT_DEFAULT, ROC_INTERFACE_AUDIO_CONTROL, + control_endp) + != 0) { + oops(); + } + + if (roc_endpoint_deallocate(control_endp) != 0) { + oops(); + } + /* Read samples from the receiver. */ for (;;) { float samples[MY_BUFFER_SIZE]; @@ -209,6 +230,26 @@ static void* sender_loop(void* arg) { oops(); } + /* Connect sender to the receiver control (RTCP) packets endpoint. */ + roc_endpoint* control_endp = NULL; + if (roc_endpoint_allocate(&control_endp) != 0) { + oops(); + } + + roc_endpoint_set_protocol(control_endp, ROC_PROTO_RTCP); + roc_endpoint_set_host(control_endp, MY_RECEIVER_IP); + roc_endpoint_set_port(control_endp, MY_RECEIVER_CONTROL_PORT); + + if (roc_sender_connect(sender, ROC_SLOT_DEFAULT, ROC_INTERFACE_AUDIO_CONTROL, + control_endp) + != 0) { + oops(); + } + + if (roc_endpoint_deallocate(control_endp) != 0) { + oops(); + } + /* Prepare some non-zero samples. */ float samples[MY_BUFFER_SIZE]; int i;